yii2 数据模型 -- 基于场景的验证

  • 目录 {:toc}

系统交互的过程中,对数据的接收方来说,对数据的格式、内容的校检、验证至关重要。 比如后端在接受注册用户的数据时候,会对传送过来的邮箱、手机号、用户密码、填 写的外链url、注册地点ip等每种格式都需要验证是否符合对应的规范,有时还需要根据 数据库中的现有数据,判断新注册的登陆用户名是否重复,有时不只是单纯的验证某一项, 需要依赖对其他的项的验证结果来决定是否验证,比如邮箱跟手机号两个二选一即可。 有时换到另外一种场景,如在更新用户信息的时候,仔细观察后就会发现上述验证规则 有很大的重复。

针对这些问题,在yii2中是使用元编程的思路,给出了一种很优雅的解决方式 --- Validator。

yii2 的Validator实现跟数据模型Model有很多的互相交互、依赖。下面给出以下从Model 层面的使用样例。

样例

针对上述提到的几个问题,在yii2中的解法如下,仅仅使用了一个数组就描述清楚的每个字段在 不同场景下的验证规则,关于核心验证器的用法参考yii2权威指南-核心验证器相关章节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

class User extends yii\base\Model {
public $mobile;
public $email;
public $username;
public $password;
public $reg_ip;
public $weibo_url;

const SCENARIO_REG = 'reg';

public function rules() {
return [
//邮箱、手机号必有一个不为空
[['email'], 'required', 'when' => function(self $model) {
return !(new \yii\validators\RequiredValidator())
->validate($model->mobile);
}],
//检查邮箱、ip的数据格式
['email', 'email'],
['reg_ip', 'ip'],
//数据库中值唯一
['username','unique', 'on' => [
self::SCENARIO_REG
], 'message' => '你的用户名TMD热门了,已被占用,再换一个吧!!!'],
//用户名,密码长度至少为8,最多为16
['username', 'string','length' => [8,16]],
['password', 'string', 'min' => 8, 'max' => 20],

//用户名只含有52英文字母表字幕及数字,且只能以字母开始
['username', 'match', 'pattern' => '/[a-zA-Z]+[a-zA-Z0-9]*/'],
];
}

}

$model = new User();
$model->setScenario('default');
$model->load($_POST, '');
if(!$model->validate()) {
Yii::error($model->getErrors(),'DATA_VALIDATION');
...
}

...

rules()

rules()返回结果是一个数组,其中的每一个数组元素描述了一条验证规则。每个元素的格式一般如下:

[attributes [] | String, Validator | Closure | String, Validator子类实例初始化列表] 通过下面的createValidtor()源码可以看出,其根据rule()返回的每条验证规则,构造对应的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Creates validator objects based on the validation rules specified in [[rules()]].
* Unlike [[getValidators()]], each time this method is called, a new list of validators will be returned.
* @return ArrayObject validators
* @throws InvalidConfigException if any validation rule configuration is invalid
*/
public function createValidators()
{
$validators = new ArrayObject();
foreach ($this->rules() as $rule) {
if ($rule instanceof Validator) {
$validators->append($rule);
} elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
$validator = Validator::createValidator($rule[1], $this, (array) $rule[0], array_slice($rule, 2));
$validators->append($validator);
} else {
throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
}
}

return $validators;
}

每个元素的可以取值类型如下:

  • attributes [] | String , 如果只有一个属性,其可以是一个字符串,否则为数组形式
  • Validator | Closure | String, 此处可以为yii2的子类实例, 或是是 Yii2 内部实现的一组核心验证器(都是Validator的子类)的别名, 如果没有这个别名的验证器,就会使用数据模型 Model 子类实例中同名的方法,最后,其可以直接是一个闭包。后两种方式都是封装在一个InlineValidator中实现的。
  • 再往后就是一些关联数组,每一对都是针对每个Validator属性的初始化。

如此清晰易读的语法形式和初始化列表功能的存在,完全得益于yii2的基于配置这个特性。

场景 scenario , 属性 attribute

场景本身也没什么,就是用来告诉验证器当前是在哪个场景之下,例如注册用户、更新用户信息可以设置两个场景。 实际使用时,通过setScenario 设置完场景,在后面调用 validate时 yii2 就可以自动选定对应场景下的规则进行验证, 上列中用on指定规则在那些场景下启用,上例中没有使用on的规则,在所有场景下都会进行验证其内的属性。

rule()方法返回结果数组中每条验证规则内的活动属性(attribute),既用来说明那些属性是安全的,可以对类中同名成员变量 进行块赋值, 也表示,调用validate时,将会使用本条规则对活动属性进行验证。

load()

本例是对rule返回安全的活动属性进行块赋值到类实例的成员变量,

on except 属性:

on是用来指定当前的验证规则是在那些场景下启用。

  • 没有指定 on 属性的字段,规则会在所有场景中都被启用
  • on的值可以为字符串,'scenarino' 单个场景中起作用
  • on的值可以为数组['scenrino1',scenarino2]多个场景中起作用

excepton 类似,只是用来说明那些场景不启用当前规则。有个值得注意的问题就是onexcept同时指定的时候只有on才会起效。

message 属性

当针对属性的验证规则失败的时候,用于指定自定义的错误返回信息

scenarios()

这个数据模型 Model 的方法返回每个场景及其对应的活动属性(active attribute)数组。

1
2
3
4
5
[
'scenario1' => ['active attribute1','active attribute2' ]
'scenario2' => ['active attribute1','active attribute2' ]
'scenario3' => ['active attribute1','active attribute2' ]
]

在使用setScenario设置完当前的场景后,scenarios()返回的场景对应的安全的活动属性可以进行块赋值(即同时可以对多个属性进行赋值) ,上例中使用load() 进行的块赋值。如果不想在 scenario1中对某些活动属性使用块赋值,只需要在属性名前加!标记为非安全的活动属性即可。

1
'scenario1' => ['!active attribute1','active attribute2' ]

如果这个方法没有被覆盖重写,默认的Model中的返回值是根据rule()的返回数组生成的。 其中:

  • 有一个默认场景default,所有没有使用 on except的其中的属性的都属于此场景
  • 返回rules里面发现的所有场景和对应的属性,默认是属性是安全的可以进行块赋值
  • 可以在每个属性前加一个!标记属性是非安全的,不使用块赋值。

总结

以上就是数据模型基于场景的验证的简单分析。


yii2 数据模型 -- 基于场景的验证
http://blog.soul11201.com/2018/08/07/yii2-rules/
作者
soul11201
发布于
2018年8月7日
许可协议