YII 驗證和消息
<div class="yiiForm"> <?php echo CHtml::form(); ?> <table> <tr><th>名稱</th><th>價格</th><th>數量</th><th>描述</th></tr> <?php foreach($items as $i=>$item): ?> <tr> <td><?php echo CHtml::activeTextField($item,"name[$i]"); ?></td> <td><?php echo CHtml::activeTextField($item,"price[$i]"); ?></td> <td><?php echo CHtml::activeTextField($item,"count[$i]"); ?></td> <td><?php echo CHtml::activeTextArea($item,"description[$i]"); ?></td> </tr> <?php endforeach; ?> </table> <?php echo CHtml::submitButton('Save'); ?> <?php echo CHtml::endForm(); ?> </div>
setFlash(), getFlash()可以完成驗證成功后提示
<?php # 成功信息提示 Yii::app()->user->setFlash('success', "Thinks saved success!"); # 錯誤信息提示 Yii::app()->user->setFlash('error', "here has an Error, Please check that!"); # 一般消息信息提示 Yii::app()->user->setFlash('notice', "messge here"); ?>
2.errorSummary驗證不通過的錯誤提示
rules()方法中定義的規則會在模型實例調用其 validate() 或 save() 方法時逐一執行。normalizeTags驗證器是一個基於方法的驗證器,Models/xx.php中的rules()的驗證規則是對數據庫表進行的,Models/xxForm.php中的rules()的驗證規則是對表單進行的,和數據庫表沒有關系,類名和文件名要相同 。
如果我們使用一個validator(驗證器)類,則這個類必須繼承CValidator。其實有三種方法可以指定validator(驗證器),包括前面提到的一種格式:
1.第一種是在模型類中定義驗證方法
2.第二種是指定一個單獨的驗證器類(這個類繼承validators/CValidator )。
3.第三種是你可以使用Yii框架中現有的驗證器,指定預定義的驗證器別名即可。
Yii為你提供了很多預定義的驗證器類,同時也指定了別名,用在定義規則時。Yii1.1版本,預定義的驗證器別名的完整列表如下:
* captcha:它是CCaptchaValidator類的別名,驗證屬性的值等於一個顯示的CAPTCHA(驗證碼)的值。
* compare:它是CCompareValidator類的別名'=','==','!=','>','>='。
* default:它是CDefaultValidator類的別名,驗證屬性的值為分配的默認值。
* exist:它是CExistValidator類的別名,驗證屬性的值在表中的對應列中存在。
* filter:它是CFilterValidator類的別名,用過濾器轉換屬性的值。
* in:它是CRangeValidator類的別名,驗證屬性值在一個預定義列表中。
* length:它是CStringValidator類的別名,驗證屬性值的長度在一個范圍內。
* match:它是CRegularExpressionValidator類的別名,驗證屬性值匹配一個正則表達式。
* numerical:它是CNumberValidator類的別名,驗證屬性值是數字。
* type:它是CTypedValidator類的別名,驗證屬性值是一個指定的數據類型。
* unique:它是CUniquedValidator類的別名,驗證屬性值在表中的對應列中是唯一的。
* url:它是CUrlValidator類的別名,驗證屬性值是一個有效的URL。
2.單獨的驗證器類 方便重用
首先要做的是創建類文件.最好的方法時類的文件名和類名相同,可以使用yii的延遲加載(lazy loading)功能。
讓我們在應用(application)的擴展(extensiions)目錄(在 protected 文件夾下)下新建一個文件夾.
將目錄命名為: MyValidators然后創建文件: passwordStrength.php
class passwordStrength extends CValidator{ public $strength; private $weak_pattern = '/^(?=.*[a-zA-Z0-9]).{5,}$/'; private $strong_pattern = '/^(?=.*\d(?=.*\d))(?=.*[a-zA-Z](?=.*[a-zA-Z])).{5,}$/'; protected function validateAttribute($object,$attribute) { // check the strength parameter used in the validation rule of our model if ($this->strength == 'weak') $pattern = $this->weak_pattern; elseif ($this->strength == 'strong') $pattern = $this->strong_pattern; // extract the attribute value from it's model object $value=$object->$attribute; if(!preg_match($pattern, $value)) { $this->addError($object,$attribute,'your password is too weak!'); } }
然后在模型(model)的:
/** * @return array validation rules for model attributes. */ public function rules() { return array( array('password', 'ext.MyValidators.passwordStrength', 'strength'=>self::STRONG), ); }
由於我們直接在User AR類中添加了$repassword屬性,並且它與底層數據庫表之間沒有對應關系,我們需要告訴模型類允許這個屬性在setAttributes()被調用時被設置。 保存時不能入庫需要添加rules驗證驗證器 。我們的做法是將其添加到User模型類的安全屬性列表中。向User::rules()數組添加下列代碼:
array('repassword', 'safe'),
我們新建的$repassword不存在對應的tbl_user表中的列,需要將其直接添加到安全屬性列表。
setAttributes:
$model->attributes=$_POST['User'];
添加了$repassword屬性
class User extends CActiveRecord { public $repassword; //不能是private會報錯
以注冊驗證為例.controller
public function actionRegister() { $model=new User; if(isset($_POST['User'])) { $model->attributes=$_POST['User']; if($model->save()){ Yii::app()->user->setFlash('register','Thank you for your register.');//驗證通過提示 $this->refresh(); } } $this->render('register',array( 'model'=>$model, )); }
register.php
<h1>注冊用戶</h1> <?php if(Yii::app()->user->hasFlash('register')): ?> <div class="flash-success"> <?php echo Yii::app()->user->getFlash('register'); ?> </div> <?php else: ?> <div class="form"> <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'user-form', 'enableAjaxValidation'=>false, )); ?> <p class="note">Fields with <span class="required">*</span> are required.</p> <?php echo $form->errorSummary($model); ?> <div class="row"> <?php echo $form->labelEx($model,'password'); ?> <?php echo $form->passwordField($model,'password',array('size'=>60,'maxlength'=>128, 'autocomplete'=>'off','value'=>'')); ?> <?php echo $form->error($model,'password'); ?> </div> <div class="row"> <?php echo $form->labelEx($model,'repassword'); ?> <?php echo $form->passwordField($model,'repassword',array('size'=>60,'maxlength'=>128, 'autocomplete'=>'off','value'=>'')); ?> <?php echo $form->error($model,'repassword'); ?> </div> <div class="row buttons"> <?php echo CHtml::submitButton('注冊'); ?> </div> <?php $this->endWidget(); ?> </div> <?php endif; ?>
ajax動態驗證
第一步:在_form中最上面改成
$form=$this->beginWidget('CActiveForm', array( 'id'=>'user-form', 'enableAjaxValidation'=>true, )
第二步:controller中添加,對應 'id'=>'user-form'
$this->performAjaxValidation($model); protected function performAjaxValidation($model) { if(isset($_POST['ajax']) && $_POST['ajax']==='user-form') { echo CActiveForm::validate($model); Yii::app()->end(); } }
第三步:在models層中加入checkemai方法
public function rules() { // NOTE: 可以用exist驗證器替換 return array( array('email', 'checkUser','message'=>'Test message for email validation'), array('user_id', 'checkUser','message'=>'Test message for {attribute} validation'), ); } public function checkUser($attribute,$params) //attribute用法 { switch($attribute){ case "email": //rules email $models = ServiceReviews::model()->findAllByAttributes(array('email' =>$this->email,'service_id'=>$this->service_id)); if(count($models)>0){ $this->addError($attribute, $params['message']); } break; case "user_id": if(Yii::app()->user->isGuest){ $models = ServiceReviews::model()->findAllByAttributes(array('user_id' =>Yii::app()->user->id,'service_id'=>$this->service_id)); if(count($models)>0){ $this->addError($attribute, $params['message']); } } break; } }
最后在models層的驗證規則中(rules)加入以下驗證規則{attribute}
array('email', 'checkUser','message'=>'已經存在{attribute}'),
剛才創建的方法需要兩個參數:
* $attribute 需要驗證的屬性
* $params 在規則中自定義的參數
在模型的 rules 方法中我們驗證的是email屬性,所以在驗證規則中需要驗證的屬性值應該是 email.
在 rules 方法中我們還設置了自定義的參數 message,它的值將會放到 $params 數組中.
三.非表單驗證錯誤處理 :
你會發現在方法中我們使用了 CModel::addError().添加錯誤接受兩個參數:第一個參數是在表單中顯示錯誤的屬性名,第二個參數時顯示的錯誤信息 。
用戶提交表單時,可能除表單驗證之外還有與表單各輸入項無關的其他錯誤產生,例如后台數據庫出錯、接口調用失敗等。
這種情況下可以在Model中相應的位置使用如下代碼記錄錯誤:
$this->addError('info', '發送不明錯誤,請重試'); // info 只是一個自定義的名字,不需要真正有這個字段或屬性。
然后在視圖文件中這樣輸出錯誤:
echo $form->error($model, 'info'); //$form 是 CActiveForm 的實例。$form->getErrors();
當我們調用 CModel::validate() 方法, 我們可以指定一個場景參數. 只有在特定的場景下校驗規則才會生效.校驗規則會在那些 on 選項沒有被設置或者包含了指定的場景名稱的場景中生效.如果我們沒有指定場景,而調用了 CModel::validate() 方法,只有那些 on 選項沒有設置的規則才會被執行 .
$model = new model('register'); // or $model=new User; // $model->scenario='register';
例如,在注冊一個用戶時,我們運行以下腳本來執行校驗 :
array('password', 'compare', 'compareAttribute'=>'repassword', 'on'=>'register,edit'),
Email驗證器
array('email','email'), //驗證email這個字段必須符合email格式
Compare驗證器
array('password2','compare','compareAttribute'=>'password1'),//驗證password1和password2必須一致 array('end_date','compare', 'compareAttribute' => 'start_date', 'operator' => '>', 'message' => '錯誤的開始結束日期'),
Unique驗證器
array('username,email','unique','className'=>'User'),//User為Model,username,email在user中不允許重復
如果被驗證屬性為空,就認為完全合法,立刻返回,但是如果allowEmpty為false的話,就要通過函數后續的所有驗證條件 。那么對於一個傳入的空值來說,allowEmpty無論是true還是false,極有可能都不會報錯,上面節選的驗證器是StringValidator,如果我沒有設定min的值,那么一個空串在allowEmpty為false的情況下,還是不會報任何錯誤的。
如果希望一個屬性值不能為空,最好還是用RequiredValidator來驗證,allowEmpty是不靠譜的,建議一般就采取allowEmpty的默認值true,可以節省幾次判斷。
array('verifyCode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements()),
布爾驗證器
array('rememberMe', 'boolean'),
數字驗證器
array('id', 'numerical', 'min'=>1, 'max'=>10, 'integerOnly'=>true),
default驗證器
array('created','default','value'=>new CDbExpression('NOW()'),'setOnEmpty'=>false)
fiter驗證器
array('moduleID', 'filter', 'filter'=>'trim'),
正則驗證器
array('name','match','pattern'=>'/^[a-z0-9\-_]+$/'),
in驗證器
array('superuser', 'in', 'range' => array(0, 1)),
length驗證器
array('password','length','min'=>'6','max'=>'16','message'=>'{attribute}長度必須在{min}到{max}之間'),
類型驗證 integer,float,string,array,date,time,datetime
array('created', 'type', 'datetime'),
日期格式驗證
array('created', 'date', 'format'=>'yyyy/MM/dd/ HH:mm:ss'),
文件驗證
array('filename', 'file', 'allowEmpty'=>true, 'types'=>'zip, rar, xls, pdf, ppt'),