課程112:表單驗證
課程112:表單驗證
摘要:
在這個課程中,將介紹表單欄位資料的驗證。我們在之前的課程中,一直沒有提到表單欄位驗證的問題。原因是因為,一方面是為了簡化程式的複雜性,讓學員能夠專注在學習新的技巧;另一方面,是因為表單驗證,跟物件導向程式設計,以及我們的程式開發觀念,能夠整合的更好,所以,個人認為在教完物件導向程式設計後,再回頭來教表單驗證,學習上的效果會比較好。

如何在伺服器端驗證表單欄位的資料

為什麼需要表單驗證?

一,使用者可能無意或蓄意填入不正確的資料,輸入的資料如果其資料型態與資料庫欄位定義不同,或者在不允許空值(NOT NULL)的欄位中,不填入任何資料。都會造成資料無法更新或新增的錯誤。二,有時,在使用者填寫表單時,我們希望所填入的資料,必須是某特定類型的資料,或是填入的值,必須再某特定的範圍內時,在正式寫入資料庫之前,都必須檢查,這些值是否有效。

如何把表單驗證整合在我們的程式中?

表單驗證的運算邏輯應該放在哪裡?按照我們的程式開發方式,表單驗證,應該放在哪個部分?通常需要表單驗證的地方,是在新增以及更新資料時,才需要使用表單驗證。以留言版的程式而言,我們可以放在 add.php 或 update.php。但是,這樣一來,就犯了一個大忌。因為我們嘗試將程式,分為三個層面來處理,使用者介面層、運算邏輯層、資料介面層。我們可以考慮一個問題,當驗證的規則改變時,他會影響到哪個層面。比如說,在留言版中,我們硬性規定,使用者的姓名,不可以超過12個字元。請問,這個改變,會影響到畫面的呈現(使用者介面層),或者影響到資料存取的通用功能(資料介面層),還是影響到運算邏輯的處理。在這裡,表單驗證,屬於資料驗證的工作,任何跟應用程式運算規則改變有關的事情,應該都歸屬於運算邏輯層來處理。所以,比較好的方式,應該是放在運算邏輯層裡面。所以,我們應該將表單驗證,放在Guestbook.php裡面。以下,我們就將表單驗證整合在留言版的程式中。

 


將表單欄位驗證整合在留言版的程式

說明:

在這一節當中,我們將把表單驗證的功能整合在留言版的程式。

我們按照以下的步驟,來修改留言版的程式:

  1. 準備工作
  2. 修改資料庫類別檔:db.php
  3. 修改留言版類別檔:Guestbook.php
  4. 修改新增資料的程式: add.php
  5. 修改更新資料的程式: update.php
一、準備工作

說明:

將之前112課程所做好的PHP程式,放在另一個新的目錄 guestbook112 之下。

複製檔案:
  1. 使用putty連線到伺服主機。
  2. 輸入以下的命令,進入 public_html 目錄:
    cd public_html
  3. 輸入以下的命令,產生新的目錄 guestbook112,並將 guestbook111 中所有檔案,
    複製到guestbook112:
    cp -r guestbook111 guestbook112
二、產生資料庫函式檔:db.php

說明:

因為 db.php 是需存取資料庫的所有類別的父類別。我們在這裡定義一個屬性,$error,並更改Add() 和 Update()這兩個方法的功能。這樣所有繼承這個類別的子類別,自然就繼承了這個屬性,就無須自行訂定義這個屬性;同時也更新了 Add() 和 Update()這兩個方法的功能。這就是使用繼承,所帶給我們的好處。修改一個地方,其他地方的程式,跟著會作改變。

注意:因為 db.php 這個程式所定義的父類別 DB,是供網站內其他程式繼承之用。所以,我們把它放在 inc 的資料夾之內,而不是 guestbook1xx的資料夾內。

程式碼:

<?php
/* 檔名:db.php
定義資料庫父類別,供其他需要存取資料庫的類別繼承之用。
*/
class DB{
     var $add_sql;
     var $update_sql;
     var $delete_sql;
     var $select_sql;
     var $retrieve_sql;
     // 請在這個地方加入新的屬性 $error
     var $error;
     function DB(){
          $this->init();
     }
     function init(){
          mysql_pconnect(DB_HOST, DB_USER, DB_PASSWORD) 
                 || die('無法與資料庫建立連結');
          mysql_select_db(DB_DATABASE) 
                 || die('無法選擇資料庫:'.DB_DATABASE);
     }
     function Add(){

          // 在新增資料之前,先檢查資料是否有效。如果含有無效的資料時,直接傳回 false 值。
if(!$this->isDataValid()) return fals
e; $this->setSQL(); return mysql_query($this->add_sql); }
     function Update(){
   
          // 在更新資料之前,先檢查資料是否有效。如果含有無效的資料時,直接傳回 false 值。
if(!$this->isDataValid()) return fa
lse; $this->setSQL(); return mysql_query($this->update_sql); }
     function Delete(){
          $this->setSQL();
          return mysql_query($this->delete_sql);
     }
     function Select(){
          $this->setSQL();
          return $this->recordset($this->select_sql);
     }
               
     function recordset($sql){
          $rs = array();
          $result = mysql_query($sql);
          if($result){
               while($record = mysql_fetch_assoc($result)){
               array_push($rs, $record);
               }
               return $rs;
          }else{
               return false;
          }
     }
     function Retrieve(){
          $this->setSQL();
          $result = mysql_query($this->retrieve_sql);
          if($result and mysql_num_rows($result) == 1){
               return mysql_fetch_assoc($result);
          }else{
               return false;
          }
     }
}
?>
三、修改留言版類別檔:Guestbook.php

說明:

之前在父類別 DB修改了Add() 和 Update()這兩個方法,在實際新增以及更新資料之前,我們呼叫了 isDataValid()這個方法。然而,父類別 DB本身並沒有定義這個方法 (Why?),所以我們必須在子類別中定義 isDataValid() 這個方法。

除此之外,我們定義了另一個方法 ,getProperties()。這個方法,將物件中的屬性轉換成為一個關連式陣列。方便,Update()方法更新資料失敗時,能取回原來表單的資料。讓使用者不必重新填寫所有資料。

程式碼:

<?php
/* 檔名:Guestbook.php
定義 Guestbook 類別
*/
// 藉由含括子系統的設定檔,取得網站及子系統的各種設定
include_once('app_config.php');
// 含括父類別 db.php 
include_once(INCLUDE_DIR.'db.php');
class Guestbook extends DB{
// 定義屬性
/ 通常可以用所要存取的資料表的各個欄位,作為這個類別的屬性
     var $id;
     var $name;
     var $email;
     var $web;
     var $content;
     var $post_time;
     var $fk_icon;
 // 類別的建構方法
     function Guestbook(){
          $this->DB();
     }
               
// setProperties 方法用來連接表單的欄位以及類別的屬性
     function setProperties(){
          $this->id = $_POST[id];
          $this->name = $_POST[name];
          $this->email = $_POST[email];
          $this->web = $_POST[web];
          $this->content = $_POST[content];
     }
 // setSQL 方法定義存取資料表所需的各個 SQL 命令
     function setSQL(){
          $this->add_sql = 'INSERT INTO Guestbook "; 
          $this->add_sql .= "(name, email, web, content, post_time) ';
          $this->add_sql .= "VALUES('$this->name','$this->email','$this->web',";
          $this->add_sql .= "'$this->content',NOW())";
          $this->update_sql = "UPDATE Guestbook SET name='$this->name', ";
          $this->update_sql .= "email='$this->email', web='$this->web', ";
          $this->update_sql .= "content='$this->content' WHERE id=$this->id";
          $this->delete_sql = "DELETE FROM Guestbook WHERE id=$this->id";
          $this->select_sql = "SELECT * FROM Guestbook ORDER BY post_time DESC";
          $this->retrieve_sql = "SELECT * FROM Guestbook WHERE id=$this->id";
     }

// isDataValid 方法驗證表單的資料是否有效
function isDataValid(){
if(strlen(trim($this->name)) == 0){
$this->error.= '姓名欄位不可以是空白<br/>';
}
if(strlen($this->name) > 12){
$this->error .= '姓名不可以超過 12 個字元 <br/>';
}
if(strlen(trim($this->content)) == 0){
$this->error.= '留言內容欄位不可以是空白<br/>';
}
if(strlen($this->content) > 2048){
$this->error .= '留言內容不可以超過 2048 個字元 <br/>';
}
return strlen($this->error) == 0;
}
 // 將物件的屬性轉換成一個關連式陣列。
      function getProperties(){
          return array(
               "name" => $this->name,
               "email" => $this->email,
               "web" => $this->web,
               "content" => $this->content
               );
     }

}
?>
四、修改新增資料的程式: add.php

說明:

在之前我們已經修改了 DB類別的 Add()方法,在實際新增資料之前會先呼叫子類別的 isDataValid()方法。如果,資料有問題時,會傳回 false 值。同時,將錯誤的訊息,寫入 $error 這個屬性。所以,在 add.php 中,我們可以藉由 Add() 這個方法所傳回來的值,決定是否表單的資料有誤。如果有錯誤的話,顯示錯誤訊息。錯誤的訊息會放在 $error 這個變數,然後,在 HTML碼裡面,會使用 <?php echo $error?>的方式,顯示錯誤的訊息。

同時為了避免資料錯誤時,使用者必須重新填寫每個欄位的麻煩。我們修改了每個表單欄位標籤裡面的 value屬性。

請點選 add.php 看修改過後的原始碼
五、修改更新資料的程式: update.php

說明:

在之前我們已經修改了 DB類別的 Update()方法,在實際新增資料之前會先呼叫子類別的 isDataValid()方法。如果,資料有問題時,會傳回 false 值。同時,將錯誤的訊息,寫入 $error 這個屬性。所以,在 add.php 中,我們可以藉由 Update() 這個方法所傳回來的值,決定是否表單的資料有誤。如果有錯誤的話,顯示錯誤訊息。錯誤的訊息會放在 $error 這個變數,然後,在 HTML碼裡面,會使用 <?php echo $error?>的方式,顯示錯誤的訊息。

因為,在更新的表單中各個欄位的初始值,是使用 $record 這個關連式陣列的變數。為了表單欄位值初始化的一致性,不論是從資料庫取得這個資料,或是送出表單,資料發生錯誤時,都使用 $record 這個關連式陣列的變數,來初始化欄位的值。所以,我們呼叫了 Guestbook這個類別的 getProperties() 方法。取得物件的屬性值。

請點選 update.php 看修改過後的原始碼

小組專案作業

說明:

為了讓同學有實際演練的機會,同時也驗收目前的學習成果。我們會由同學們親手實作一個專案。在撰寫程式之前,必須先了解專案的內容。要詳細了解專案的內容,就必須與使用者訪談。訪談的重點擺在聽和問。

聽啥東西呢?在使用者敘述他的問題時,重點放在他所使用的名詞和動詞。記下他所敘述的每個名詞,用箭頭來表示動詞,並連接每個名詞。名詞可能代表程式中所要設計的物件。動詞則可能是流程,或者是物件之間的互動關係。注意,在訪談過程,重點在找出每個物件他所需要具備的屬性,之後,在藉由了解各物件之間的互動關係,來定義物件的方法。

問可能比聽更重要,使用者在敘述他的問題時,通常會用比較概觀的說法。你必須透過問問題的方式,找出影響你開發程式中的細節。如果,使用者一時無法回答比較細節的問題時,告訴他你需要什麼樣的資料,請他準備好,或者與他商量如何來定義這些資料。如果,你無法開始著手開發程式時,表示你所收集的資料還不夠,直到資料收集足夠時,才可以停止問問題。

切記,訪談的重點,不在你是否有能力解決使用者的問題。而在於你能否確切了解使用者的問題。別讓你的心思沉迷在解決問題的陷阱中。別使用程式設計者的觀點來思考,要站在使用者的角度,來看這些問題。解決問題是後面的事情,不是現在你關心的重點。了解比解決更重要。