Yii2.0模型层数据验证
一般说来,程序猿永远不应该信任从最终用户直接接收到的数据,并且使用它们之前应始终先验证其可靠性。要给model填充其所需的用户输入数据,你可以调用
yii\base\Model::validate() 方法验证它们。该方法会返回一个布尔值,指明是否通过验证。若没有通过,你能通过 yii\base\Model::errors 属性获取相应的报错信息。比如
$model = new \app\models\ContactForm; // 用用户输入来填充模型的特性 $model->attributes = \Yii::$app->request->post('ContactForm'); if ($model->validate()) { // 若所有输入都是有效的 } else { // 有效性验证失败:$errors 属性就是存储错误信息的数组 $errors = $model->errors; }
声明规则(Rules)
要让 validate()
方法起作用,你需要声明与需验证模型特性相关的验证规则。为此,需要重写
yii\base\Model::rules() 方法。下面的例子展示了如何声明 用于验证 ContactForm
模型的相关验证规则:
public function rules() { return [ // name,email,subject 和 body 特性是 `require`(必填)的 [['name', 'email', 'subject', 'body'], 'required'], // email 特性必须是一个有效的 email 地址 ['email', 'email'], ]; }
yii\base\Model::rules() 方法应返回一个由规则所组成的数组,每一个规则都呈现为以下这类格式的小数组:
[ // 必须项,用于指定那些模型特性需要通过此规则的验证。 // 对于只有一个特性的情况,可以直接写特性名,而不必用数组包裹。 ['attribute1', 'attribute2', ...], // 必填项,用于指定规则的类型。 // 它可以是类名,验证器昵称,或者是验证方法的名称。 'validator', // 可选项,用于指定在场景(scenario)中,需要启用该规则 // 若不提供,则代表该规则适用于所有场景 // 若你需要提供除了某些特定场景以外的所有其他场景,你也可以配置 "except" 选项 'on' => ['scenario1', 'scenario2', ...], // 可选项,用于指定对该验证器对象的其他配置选项 'property1' => 'value1', 'property2' => 'value2', ... ]
对于每个规则,你至少需要指定该规则适用于哪些特性,以及本规则的类型是什么。你可以指定以下的规则类型之一:
- 核心验证器的昵称,比如
required
、in
、date
,等等。请参考点击打开链接。 - 模型类中的某个验证方法的名称,或者一个匿名方法。请参考行内验证器小节了解更多。
- 验证器类的名称。请参考独立验证器小节了解更多。
一个规则可用于验证一个或多个模型特性,且一个特性可以被一个或多个规则所验证。一个规则可以施用于特定场景(scenario),只要指定 on
选项。如果你不指定 on
选项,那么该规则会适配于所有场景。
当调用 validate()
方法时,它将运行以下几个具体的验证步骤:
- 检查从声明自 yii\base\Model::scenarios() 方法的场景中所挑选出的当前yii\base\Model::scenario的信息,从而确定出那些特性需要被验证。这些特性被称为激活特性。
- 检查从声明自 yii\base\Model::rules() 方法的众多规则中所挑选出的适用于当前yii\base\Model::scenario的规则,从而确定出需要验证哪些规则。这些规则被称为激活规则。
- 用每个激活规则去验证每个与之关联的激活特性。
基于以上验证步骤,有且仅有声明在 scenarios()
方法里的激活特性,且它还必须与一或多个声明自 rules()
里的激活规则相关联才会被验证。
自定义错误信息
大多数的验证器都有默认的错误信息,当模型的某个特性验证失败的时候,该错误信息会被返回给模型。比如,用 yii\validators\RequiredValidator 验证器的规则检验 username
特性失败的话,会返还给模型
"Username cannot be blank." 信息。
你可以通过在声明规则的时候同时指定 message
属性,来定制某个规则的错误信息,比如这样:
public function rules() { return [ ['username', 'required', 'message' => 'Please choose a username.'], ]; }
一些验证器还支持用于针对不同原因的验证失败返回更加准确的额外错误信息。比如,yii\validators\NumberValidator 验证器就支持 yii\validators\NumberValidator::tooBig 和 yii\validators\NumberValidator::tooSmall 两种错误消息用于分别返回输入值是太大还是太小。 你也可以像配置验证器的其他属性一样配置它们俩各自的错误信息。
验证事件
当调用 yii\base\Model::validate() 方法的过程里,它同时会调用两个特殊的方法,把它们重写掉可以实现自定义验证过程的目的:
- yii\base\Model::beforeValidate():在默认的实现中会触发 yii\base\Model::EVENT_BEFORE_VALIDATE 事件。你可以重写该方法或者响应此事件,来在验证开始之前,先进行一些预处理的工作。(比如,标准化数据输入)该方法应该返回一个布尔值,用于标明验证是否通过。
- yii\base\Model::afterValidate():在默认的实现中会触发 yii\base\Model::EVENT_AFTER_VALIDATE 事件。你可以重写该方法或者响应此事件,来在验证结束之后,再进行一些收尾的工作。
条件式验证
若要只在某些条件满足时,才验证相关特性,比如:是否验证某特性取决于另一特性的值,你可以通过 yii\validators\Validator::when 属性来定义相关条件。举例而言,
[ ['state', 'required', 'when' => function($model) { return $model->country == 'USA'; }], ]
若你需要支持客户端的条件验证,你应该配置 yii\validators\Validator::whenClient 属性,它会读入一条包含有 JavaScript 函数的字符串。这个函数将被用于确定该客户端验证规则是否被启用。比如,
[ ['state', 'required', 'when' => function ($model) { return $model->country == 'USA'; }, 'whenClient' => "function (attribute, value) { return $('#country').value == 'USA'; }"], ]
数据预处理
用户输入经常需要进行数据过滤,或者叫预处理。比如你可能会需要先去掉 username
输入的收尾空格。你可以通过使用验证规则来实现此目的。
下面的例子展示了如何去掉输入信息的首尾空格,并将空输入返回为 null。具体方法为通过调用 trim 和 default 核心验证器:
[ [['username', 'email'], 'trim'], [['username', 'email'], 'default'], ]
也还可以用更加通用的 filter(滤镜) 核心验证器来执行更加复杂的数据过滤。
如你所见,这些验证规则并不真的对输入数据进行任何验证。而是,对输入数据进行一些处理,然后把它们存回当前被验证的模型特性。
处理空输入
当输入数据是通过 HTML 表单,你经常会需要给空的输入项赋默认值。你可以通过调整 default 验证器来实现这一点。举例来说,
[ // 若 "username" 和 "email" 为空,则设为 null [['username', 'email'], 'default'], // 若 "level" 为空,则设其为 1 ['level', 'default', 'value' => 1], ]
默认情况下,当输入项为空字符串,空数组,或 null 时,会被视为“空值”。你也可以通过配置 yii\validators\Validator::isEmpty 属性来自定义空值的判定规则。比如,
[ ['agree', 'required', 'isEmpty' => function ($value) { return empty($value); }], ]
默认情况下,当输入项为空字符串,空数组,或 null 时,会被视为“空值”。你也可以通过配置 yii\validators\Validator::isEmpty 属性来自定义空值的判定规则。比如,
[ ['agree', 'required', 'isEmpty' => function ($value) { return empty($value); }], ]
注意:对于绝大多数验证器而言,若其 yii\base\Validator::skipOnEmpty 属性为默认值 true,则它们不会对空值进行任何处理。也就是当他们的关联特性接收到空值时,相关验证会被直接略过。在 核心验证器 之中,只有 captcha
(验证码),default
(默认值),filter
(滤镜),required
(必填),以及 trim
(去首尾空格),这几个验证器会处理空输入。
tp3.2模型层数据验证
thinkPHP3.2提供了两种数据验证方式
1.静态验证
所谓静态验证,就是在声明一个模型层时就声明所接收的数据的约束。形式为
<span> </span>protected $_validate = array( <span> </span>array('verify','require','验证码必须!'), //默认情况下用正则进行验证 <span> </span>array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一 <span> </span>array('value',array(1,2,3),'值的范围不正确!',2,'in'), // 当值不为空的时候判断是否在一个范围内 <span> </span>array('repassword','password','确认密码不正确',0,'confirm'), // 验证确认密码是否和密码一致 <span> </span>array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式 <span> </span>);
在数据层中声明$_validate变量,其中单独一条数据验证的格式为
<span> </span>array("验证字段","验证规则","错误提示",["验证条件","附加规则","验证时间"]),
验证字段,验证规则,错误提示是必须的。
关于验证字段,需要验证的表单字段名称,这个字段不一定是数据库字段,也可以是表单的一些辅助字段,例如确认密码和验证码等等。有个别验证规则和字段 无关的情况下,验证字段是可以随意设置的,例如verify是否输入验证码是和表单字段无关的。如果定义了字段映射的话,这里的验证字段名称应该是实际的数
据表字段而不是表单字段。
关于验证规则要进行验证的规则,需要结合附加规则,如果在使用正则验证的附加规则情况下,系统还内置了一些常用正则验证的规则,可以直接作为验证规 则使用如下:
$validate = array( 'require'=> '/.+/', 'email' => '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/', 'url' => '/^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&_~`@[\]\':+!]*([^<>\"\"])*$/', 'currency' => '/^\d+(\.\d+)?$/', 'number' => '/^\d+$/', 'zip' => '/^[1-9]\d{5}$/', 'integer' => '/^[-\+]?\d+$/', 'double' => '/^[-\+]?\d+(\.\d+)?$/', 'english' => '/^[A-Za-z]+$/', );
关于可选的验证条件,包含下面几种情况:
self::EXISTS_VALIDATE 或者0 存在字段就验证(默认)
self::MUST_VALIDATE 或者1 必须验证
self::VALUE_VALIDATE或者2 值不为空的时候验证
附加规则 (可选)
配合验证规则使用,包括下面一些规则:
规则 | 说明 |
---|---|
regex | 正则验证,定义的验证规则是一个正则表达式(默认) |
function | 函数验证,定义的验证规则是一个函数名 |
callback | 方法验证,定义的验证规则是当前模型类的一个方法 |
confirm | 验证表单中的两个字段是否相同,定义的验证规则是一个字段名 |
equal | 验证是否等于某个值,该值由前面的验证规则定义 |
notequal | 验证是否不等于某个值,该值由前面的验证规则定义(3.1.2版本新增) |
in | 验证是否在某个范围内,定义的验证规则可以是一个数组或者逗号分割的字符串 |
notin | 验证是否不在某个范围内,定义的验证规则可以是一个数组或者逗号分割的字符串(3.1.2版本新增) |
length | 验证长度,定义的验证规则可以是一个数字(表示固定长度)或者数字范围(例如3,12 表示长度从3到12的范围) |
between | 验证范围,定义的验证规则表示范围,可以使用字符串或者数组,例如1,31或者array(1,31) |
notbetween | 验证不在某个范围,定义的验证规则表示范围,可以使用字符串或者数组(3.1.2版本新增) |
expire | 验证是否在有效期,定义的验证规则表示时间范围,可以到时间,例如可以使用 2012-1-15,2013-1-15 表示当前提交有效期在2012-1-15到2013-1-15之间,也可以使用时间戳定义 |
ip_allow | 验证IP是否允许,定义的验证规则表示允许的IP地址列表,用逗号分隔,例如201.12.2.5,201.12.2.6 |
ip_deny | 验证IP是否禁止,定义的验证规则表示禁止的ip地址列表,用逗号分隔,例如201.12.2.5,201.12.2.6 |
unique | 验证是否唯一,系统会根据字段目前的值查询数据库来判断是否存在相同的值,当表单数据中包含主键字段时unique不可用于判断主键字段本身 |
验证时间(可选)
self::MODEL_INSERT或者1新增数据时候验证
self::MODEL_UPDATE或者2编辑数据时候验证
self::MODEL_BOTH或者3全部情况下验证(默认)
这里的验证时间需要注意,并非只有这三种情况,你可以根据业务需要增加其他的验证时间。
array(‘repassword‘,‘password‘,‘确认密码不正确‘,0,‘confirm‘),
这是一个用户注册表单中对确认密码repassword的检验,repassword是需要检验的字段,password是检验规则,即两次输入的密码不相符怎会返回错误信息,0表示字段存在就会验证,confirm表示判断repassword和password是否相等。
当用户使用如下的方法时
<span> </span>$User = D("user"); <span> </span>$User->create();
就会自动进行验证。如果验证后发现字段不符合,则通过
<span> </span>$User->getError();
来获取错误信息。
当然我们也可以自定义验证时间
<span> </span>array('password','checkPwd','密码错误!',1,'function',4),
调用方法为
<span> </span>$User = D("User"); // 实例化User对象 <span> </span>if (!$User->create($_POST,4)){ // 登录验证数据 <span> </span>// 验证没有通过 输出错误提示信息 <span> </span>exit($User->getError()); <span> </span>}else{ <span> </span>// 验证通过 执行登录操作 <span> </span>}
那么将会在此处使用设置了验证时间为4的规则
2.动态验证
动态验证中使用的规则格式与静态验证的相同
<span> </span>$rules = array( <span> </span>array('verify','require','验证码必须!'), //默认情况下用正则进行验证 <span> </span>array('name','','帐号名称已经存在!',0,'unique',1), // 在新增的时候验证name字段是否唯一 <span> </span>array('value',array(1,2,3),'值的范围不正确!',2,'in'), // 当值不为空的时候判断是否在一个范围内 <span> </span>array('repassword','password','确认密码不正确',0,'confirm'), // 验证确认密码是否和密码一致 <span> </span>array('password','checkPwd','密码格式不正确',0,'function'), // 自定义函数验证密码格式 <span> </span>); <span> </span>$User = M("User"); // 实例化User对象 <span> </span>if (!$User->validate($rules)->create()){ <span> </span>// 如果创建失败 表示验证没有通过 输出错误提示信息 <span> </span>exit($User->getError()); <span> </span>}else{ <span> </span>// 验证通过 可以进行其他数据操作 <span> </span>}
使用较为灵活,通常用M方法实例化模型类即可,不依赖于类的具体实现。