數據校驗
對於任何應用程序,數據校驗都是重要部分,因爲它有且於確保模型中的數據遵守了應用程序的業務規則。 例如,你可能想要確保密碼最少要有8位,或者確保用戶名唯一。 定義校驗規則使表單處理非常非常簡單。
校驗過程有許多不同的面。本節覆蓋的是其中模型這一面。 即:在調用模型中的 save() 方法時發生了什麼。 關於如何處理校驗錯誤的顯示的更多信息,參見: 表單助手。
數據校驗的第一步是在模型中建立校驗規則。這是用模型定義中的 Model::validate 數組實現的:
class User extends AppModel {
public $validate = array();
}
在上面的示例中,$validate 數組被添加到 User 模型中,但數組不包含校驗規則。 假設 users 表有 login、password、email 和 born 列,下面的示例展示了應用在這些列上的一些簡單的校驗規則:
class User extends AppModel {
public $validate = array(
'login' => 'alphaNumeric',
'email' => 'email',
'born' => 'date'
);
}
上例展示瞭如何向模型列添加校驗規則。對於 login 列,只接受字母和數字,email 必須是有效的郵件地址,born 必須是有效的日期。 如果提交的數據違反了定義的規則,這些校驗規則定義能使 CakePHP 自動在表單中顯示錯誤信息。
CakePHP 有許多校驗規則,且易於使用。 一些內置的規則允許你校驗 email、URL 和 信用卡數字的格式 - 我們稍後會講到它們的細節。
下面是一個非常複雜的校驗的示例,利用了一些內置的校驗規則:
class User extends AppModel {
public $validate = array(
'login' => array(
'alphaNumeric' => array(
'rule' => 'alphaNumeric',
'required' => true,
'message' => 'Alphabets and numbers only'
),
'between' => array(
'rule' => array('between', 5, 15),
'message' => 'Between 5 to 15 characters'
)
),
'password' => array(
'rule' => array('minLength', '8'),
'message' => 'Minimum 8 characters long'
),
'email' => 'email',
'born' => array(
'rule' => 'date',
'message' => 'Enter a valid date',
'allowEmpty' => true
)
);
}
其中兩條是爲 login 定義的:它只能包含字母和數字,並且長度必須在5至15之間。 password 列必須不少於8位長。 email 必須是有效的郵件地址,並且 born 必須是有效的日期。 還要注意的是,怎樣定義校驗失敗時顯示特定的錯誤信息。
上面的例子中,單個列可以使用多個校驗規則。如果內置的規則不能滿足你的要求,你可以添加自己需要的校驗規則。
現在你已經看到了數據校驗的全景,讓我們瞧瞧如何在模型中定義這些規則。有三種不同的方法:簡單數組、每個列的單個規則、每個列的多個規則。
簡單規則
顧名思義,這是定義校驗規則最簡單的方法。 這種方法定義規則是標準語法是:
public $validate = array('fieldName' => 'ruleName');
‘fieldName’ 是規則所適用的列的名字,‘ruleName’是預定義的規則名,例如 ‘alphaNumeric’、’email’ 或者 ‘isUnique’。
例如,要確保用戶所提供的是格式正確的郵件地址,可以使用如下規則:
public $validate = array('user_email' => 'email');
每個列一個規則
這種定義手法對校驗規則的工作有更好的控制。在我們討論這個之前,先來看看向單個列添加一條規則的標準用法:
public $validate = array(
'fieldName1' => array(
'rule' => 'ruleName', // or: array('ruleName', 'param1', 'param2' ...)
'required' => true,
'allowEmpty' => false,
'on' => 'create', // or: 'update'
'message' => 'Your Error Message'
)
);
‘rule’ 鍵是必須的。如果僅設置了 ‘required’ => true,表單驗證將無法正確工作。因爲 ‘required’ 不是實際的規則。
正如你看到的,每個列(上面只演示了一個列)與包含了如下五個鍵的數組關聯:‘rule’、 ‘required’、 ‘allowEmpty’、 ‘on’ 和 ‘message’。讓我們仔細地觀察這幾個鍵。
rule
‘rule’ 方法定義了校驗方面並且指定了一個值或者一個數組。這個特定的 ‘rule’ 可能是模型中的一個方法的名字,核心 Validation 類的一個方法的名字,或者正則表達式。要了解關於默認規則的更多信息,請參見 內核覈驗規則。
如果 rule 不包含任何參數,’rule’ 可以是單個值,例如:
public $validate = array(
'login' => array(
'rule' => 'alphaNumeric'
)
);
如果 rule 包含參數(例如 max,min 或者範圍),’rule’ 將是一個數組:
public $validate = array(
'password' => array(
'rule' => array('minLength', 8)
)
);
記住,用數組方式定義規則時,’rule’ 鍵是必須的。
required
這個鍵接受一個邏輯值、create 或者 update。將其設置爲 true 將使這一列總是被必須的。設置爲 create 或者update 將使這一列只在更新或創建操作時必須。如果 ‘required’ 等於 true,數組中必須提供此列。例如,如果定義如下校驗規則:
public $validate = array(
'login' => array(
'rule' => 'alphaNumeric',
'required' => true
)
);
傳遞給模型的 save() 方法的數據必須包含提供給 login 列的數據。如果它不存在,那麼校驗失敗。這個鍵的默認值爲邏輯 false。
required => true 與 校驗規則中的 notEmpty() 不是一回事兒。required => true 表示這個數組 鍵 必須提供 - 不意味着它必須有值。如果數據集沒有提供,校驗失敗,但是如果提交了空值(’‘)是有可能成功的(這依賴於規則的詳細定義)。
在 2.1 版更改: 添加了對 create 和 update 的支持。
allowEmpty
如果設置爲 false,這個列的值必須是 非空的,, the field value must be nonempty, 其中 “nonempty” 非空的定義爲 !empty($value) || is_numeric($value)。 對於數值檢測,當 $value 爲0時,CakePHP 認爲是正確的。
required 與 allowEmpty 的不同可能會造成混亂。'required' => true 意味着 $this->data 中不提供帶有這個列的鍵 就不能保存模型; 而 'allowEmpty' => false 則像上面描述的尋,是確保這個列的 值 必須是非空的。
on
‘on’ 鍵可以被設置爲 ‘update’ 或 ‘create’。它提供了允許一些規則應用於創建新記錄的過程中或者更新記錄的過程中的機制。
如果一條規則被定義成 ‘on’ => ‘create’,這條規則僅在創建新記錄的過程中有效。類似的,如果它被定義成 ‘on’ => ‘update’,將只在更新記錄的過程中有效。
‘on’ 的默認值爲空(null)。當 ‘on’ 爲空(null),這條規則在創建和更新過程中同時生效。
message
message 鍵爲規則自定義校驗錯誤時的顯示信息:
public $validate = array(
'password' => array(
'rule' => array('minLength', 8),
'message' => 'Password must be at least 8 characters long'
)
);
每個列多條規則
上面的技術爲我們提供了比簡單的規則分配更大的靈活性,再進一步,我們能獲得更詳細的數據校驗控制。下面我們介紹一種允許我們爲每個列賦予多個規則的技術。
想要爲單個列賦多個校驗規則,基本的寫法如下:
public $validate = array(
'fieldName' => array(
'ruleName' => array(
'rule' => 'ruleName',
// 類似 on,required 等擴展鍵放在這裏...
),
'ruleName2' => array(
'rule' => 'ruleName2',
// 類似 on,required 等擴展鍵放在這裏...
)
)
);
正像你看到的那樣,這和上一節中所做的非常相似。在那兒,每個列僅有一個校驗參數數組。在這兒,每個 ‘fieldName’ 是一個規則數組的索引。每個 ‘ruleName’ 包含一個校驗參數數組。
下面是一個帶有實際例子的更好詮釋:
public $validate = array(
'login' => array(
'loginRule-1' => array(
'rule' => 'alphaNumeric',
'message' => 'Only alphabets and numbers allowed',
),
'loginRule-2' => array(
'rule' => array('minLength', 8),
'message' => 'Minimum length of 8 characters'
)
)
);
上例爲 login 列定義了兩個規則: loginRule-1 和 loginRule-2。如你所見,每個規則的標識隨意命名。
在爲單個列定義多條規則時,’required’ 和 ‘allowEmpty’ 鍵僅需在第一個規則中使用一次。
last
對於每個列有多條規則的情況,默認的如果一個規則校驗失敗並返回錯誤消息,該列的後續規則將不再計算。如果希望一個規則失敗了校驗仍然繼續,就將這條規則的 last 鍵設置爲 false。
在下面的例子中,即使 “rule1” 失敗,”rule2” 仍將計算,並且在 “rule2” 也失敗的時候返回所有的錯誤信息:
public $validate = array(
'login' => array(
'rule1' => array(
'rule' => 'alphaNumeric',
'message' => 'Only alphabets and numbers allowed',
'last' => false
),
'rule2' => array(
'rule' => array('minLength', 8),
'message' => 'Minimum length of 8 characters'
)
)
);
在用數組指定校驗規則時,可以不使用 message 鍵。考慮下面的例子:
public $validate = array(
'login' => array(
'Only alphabets and numbers allowed' => array(
'rule' => 'alphaNumeric',
),
)
);
如果 alphaNumeric 規則失敗,由於沒有設置 message 鍵,這條規則的鍵 ‘Only alphabets and numbers allowed’ 將作爲錯誤消息被返回。
自定義校驗規則
如果找不到所需的校驗規則,可以自定義。有兩種方法進行自定義: 自定義正則表達式,或者創建自定義校驗方法。
自定義校驗正則表達式
如果所需的校驗手段能夠通過正則表達式匹配來完成,可以自定義表達式作爲列的校驗規則:
public $validate = array(
'login' => array(
'rule' => '/^[a-z0-9]{3,}$/i',
'message' => 'Only letters and integers, min 3 characters'
)
);
上例檢查了 login 是否僅包含字母和整數,並且不得小於3個字符。
“rule” 中的正則表達式必須用斜槓(/)括住。末尾的可選項 ‘i’ 表示正則表達式是不區分大小寫的。
添加自己的校驗方法
有時僅用正則模式校驗數據還不夠。 例如,想要確保促銷碼僅能被使用 25 次,需要添加自己的校驗方法,示例如下:
class User extends AppModel {
public $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'This code has been used too many times.'
)
);
public function limitDuplicates($check, $limit) {
// $check 的值: array('promotion_code' => 'some-value')
// $limit 的值: 25
$existing_promo_count = $this->find('count', array(
'conditions' => $check,
'recursive' => -1
));
return $existing_promo_count < $limit;
}
}
被校驗的當前列被傳遞給函數,作爲該函數的第一個參數,是以列名爲鍵,以 post 來的數據爲值構成的關聯數組。
如果想要給校驗函數傳遞額外的參數,向 ‘rule’ 數組添加一個元素,並在函數中以擴展參數來處理它們(排在主參數$check 之後)。
校驗函數可以放在模型中(就像上面的例子),也可以放在模型實現的行爲中。包括映射方法。
模型/行爲 方法最先校驗,優先於在 Validation 類的方法。這意味着可以在應用程序級別(通過在 AppModel 添加方法)或模型級別覆蓋已存在的校驗方法(例如 alphaNumeric())。
在編寫可用於多個列的校驗規則,從 $check 數組中提取列值時要小心。$check 數組是以表單域名作爲鍵、表單域值作爲值構成的。存儲在 $this->data 成員變量中的整個記錄被校驗。
class Post extends AppModel {
public $validate = array(
'slug' => array(
'rule' => 'alphaNumericDashUnderscore',
'message' => 'Slug can only be letters, numbers, dash and underscore'
)
);
public function alphaNumericDashUnderscore($check) {
// $data 數組是以表單域名爲鍵被傳遞的
// 必須提取該值以使函數通用
$value = array_values($check);
$value = $value[0];
return preg_match('|^[0-9a-zA-Z_-]*$|', $value);
}
}
註解
自定義的校驗方法的可見性必須是 public。不支持 protected 和 private 級別的校驗方法。
如果規則有效,該方法返回 true。如果校驗失敗,返回 false。另一個有效的返回值是一個字符串,它是要顯示的錯誤信息。返回字符串意味着校驗失敗。 這個字符串將覆蓋 $validate 數組中的信息集,並將作爲列爲什麼無效的原因顯示在視圖的表單中。
動態改變校驗規則
使用 $validate 屬性定義校驗規則是爲每個模型定義靜態規則的好辦法。不過有時你需要從預定義的規則集中動態添加、改變或刪除校驗規則。
所有的校驗規則都存儲在 ModelValidator 對象中,它掌握着模型中各個列的每個規則集。 通過通知這個對象爲想要的列存儲新的校驗方法來定義新的校驗規則是很簡單的。
添加新的校驗規則
2.2 新版功能.
ModelValidator 對象提供了數個向集中添加新列的途徑。第一個是使用 add 方法:
// 在模型類中
$this->validator()->add('password', 'required', array(
'rule' => 'notEmpty',
'required' => 'create'
));
這樣就會爲模型中的 password 列添加單一規則。可以鏈式多次調用 add 來創建多條所需的規則:
// 在模型類中
$this->validator()
->add('password', 'required', array(
'rule' => 'notEmpty',
'required' => 'create'
))
->add('password', 'size', array(
'rule' => array('between', 8, 20),
'message' => 'Password should be at least 8 chars long'
));
也可以一次性爲單個列添加多條規則:
$this->validator()->add('password', array(
'required' => array(
'rule' => 'notEmpty',
'required' => 'create'
),
'size' => array(
'rule' => array('between', 8, 20),
'message' => 'Password should be at least 8 chars long'
)
));
或者也可以利用數組接口使用此對象,直接爲列設置規則:
$validator = $this->validator();
$validator['username'] = array(
'unique' => array(
'rule' => 'isUnique',
'required' => 'create'
),
'alphanumeric' => array(
'rule' => 'alphanumeric'
)
);
編輯現有的校驗規則
2.2 新版功能.
可以使用 validator 對象編輯現有的校驗規則,有幾種方法可以修改現有規則、向列追加方法或者從列規則集中完整地刪除一條規則:
// 在模型類中
$this->validator()->getField('password')->setRule('required', array(
'rule' => 'required',
'required' => true
));
也可以使用類似的方法完整地替換一個列的所有規則:
// 在模型類中
$this->validator()->getField('password')->setRules(array(
'required' => array(...),
'otherRule' => array(...)
));
如果只是想領回規則中的單個屬性,可以直接設置 CakeValidationRule 對象的屬性:
// 在模型類中
$this->validator()->getField('password')
->getRule('required')->message = 'This field cannot be left blank';
CakeValidationRule 中的屬性是由在模型中使用 $validate 屬性定義相應規則的有效數組鍵來命名的。
在向規則集中添加規則時,也可以使用數組接口編輯已有的規則:
$validator = $this->validator();
$validator['username']['unique'] = array(
'rule' => 'isUnique',
'required' => 'create'
);
$validator['username']['unique']->last = true;
$validator['username']['unique']->message = 'Name already taken';
從規則集中刪除規則
2.2 新版功能.
完整地刪除一個列的所有規則和只刪除一個列規則集中的單個規則都是可以的:
// 完成地刪除一個列的所有規則
$this->validator()->remove('username');
// 從 password 中刪除 'required' 規則
$this->validator()->remove('password', 'required');
還可以使用數組接口從規則集中刪除規則:
$validator = $this->validator();
// 完整地刪除一個列的所有規則
unset($validator['username']);
// 從 passworkd 中刪除 'required' 規則
unset($validator['password']['required']);