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'),
