Yii 模型的 rules() 內建驗證器已涵蓋大多數場景,但業務邏輯常需查詢資料庫(如檢查標籤是否重複)、跨欄位比對等自訂規則。本文由淺入深介紹三種實作層級:模型內行內方法、獨立 CValidator 衍生類別、支援用戶端驗證的完整元件化方案。
視圖片段:
<?php $form=$this->beginWidget('CActiveForm', ['id'=>'tag-form', 'enableAjaxValidation'=>false]); ?>
<div class="row">
<?php echo $form->labelEx($model,'tagname'); ?>
<?php echo $form->textField($model,'tagname',['size'=>20,'maxlength'=>32]); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model,'tagtype'); ?>
<?php echo $form->radioButtonList($model,'tagtype',[1=>'一般 TAG',2=>'系統預設 TAG'],['separator'=>'','labelOptions'=>['class'=>'tagtypelabel']]); ?>
</div>
<?php echo $form->errorSummary($model); ?>
<div class="row buttons">
<?php echo CHtml::submitButton($model->isNewRecord ? '新增' : '修改'); ?>
</div>
<?php $this->endWidget(); ?>
步驟 1:在 rules() 宣告規則
public function rules() {
return [
['tagname, tagtype', 'required'],
['tagtype', 'numerical', 'integerOnly' => true],
['tagname', 'length', 'max' => 32],
['tagname', 'match', 'pattern' => '/^[\x{4e00}-\x{9fa5}A-Za-z0-9]+$/u', 'message' => '標籤只能含中英數字!'],
['tagname', 'checkTagName', 'on' => 'create,update'], // 自訂規則
['tagid, tagname, tagtype', 'safe', 'on' => 'search'],
];
}
步驟 2:實作驗證方法
public function checkTagName($attribute, $params) {
$existing = Tag::model()->findByAttributes(['tagname' => $this->tagname]);
if ($existing && $existing->tagid > 0) {
$this->addError($attribute, '該標籤已存在!');
}
}
重點:
($attribute, $params)。$this->addError($attribute, '訊息') 將錯誤掛在對應屬性,視圖自動顯示。'on' => 'create,update' 限定場景,避免搜尋時誤觸發。class User extends CActiveRecord {
const WEAK = 0;
const STRONG = 1;
public function rules() {
return [
['password', 'passwordStrength', 'strength' => self::STRONG],
];
}
public function passwordStrength($attribute, $params) {
$pattern = $params['strength'] === self::WEAK
? '/^(?=.*[a-zA-Z0-9]).{5,}$/'
: '/^(?=.*\d(?=.*\d))(?=.*[a-zA-Z](?=.*[a-zA-Z])).{5,}$/';
if (!preg_match($pattern, $this->$attribute)) {
$this->addError($attribute, '密碼強度不足!');
}
}
}
規則中傳入的參數(strength)會自動放入 $params 陣列。
1. 建立檔案:protected/extensions/MyValidators/PasswordStrengthValidator.php
class PasswordStrengthValidator extends CValidator {
public $strength = 'weak';
private $weakPattern = '/^(?=.*[a-zA-Z0-9]).{5,}$/';
private $strongPattern = '/^(?=.*\d(?=.*\d))(?=.*[a-zA-Z](?=.*[a-zA-Z])).{5,}$/';
protected function validateAttribute($object, $attribute) {
$pattern = $this->strength === 'strong' ? $this->strongPattern : $this->weakPattern;
$value = $object->$attribute;
if (!preg_match($pattern, $value)) {
$this->addError($object, $attribute, '密碼強度不足!');
}
}
// 選擇性:支援 CActiveForm::enableClientValidation
public function clientValidateAttribute($object, $attribute) {
$pattern = $this->strength === 'strong' ? $this->strongPattern : $this->weakPattern;
return "
if (!value.match({$pattern})) {
messages.push(" . CJavaScript::encode('密碼強度不足!') . ");
}
";
}
}
2. 在模型中使用:
public function rules() {
return [
['password', 'ext.MyValidators.PasswordStrengthValidator', 'strength' => 'strong'],
];
}
優勢:
clientValidateAttribute() 即可產出 JavaScript 驗證碼,配合 CActiveForm::enableClientValidation=true 實現瀏覽器端即時檢查(Yii 1.1.7+)。checkXxx)。CValidator 類別。善用驗證層級,讓 rules() 保持精簡,業務邏輯集中、可測試、可複用。