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

阅读数:59 2019 年 9 月 23 日 09:48

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

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

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

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

样例

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

复制代码
1class User extends yii\base\Model {
2 public $mobile;
3 public $email;
4 public $username;
5 public $password;
6 public $reg_ip;
7 public $weibo_url;
8
9 const SCENARIO_REG = 'reg';
10
11 public function rules() {
12 return [
13 // 邮箱、手机号必有一个不为空
14 [['email'], 'required', 'when' => function(self $model) {
15 return !(new \yii\validators\RequiredValidator())
16 ->validate($model->mobile);
17 }],
18 // 检查邮箱、ip 的数据格式
19 ['email', 'email'],
20 ['reg_ip', 'ip'],
21 // 数据库中值唯一
22 ['username','unique', 'on' => [
23 self::SCENARIO_REG
24 ], 'message' => '你的用户名 TMD 热门了,已被占用,再换一个吧!!!'],
25 // 用户名,密码长度至少为 8,最多为 16
26 ['username', 'string','length' => [8,16]],
27 ['password', 'string', 'min' => 8, 'max' => 20],
28
29 // 用户名只含有 52 英文字母表字幕及数字,且只能以字母开始
30 ['username', 'match', 'pattern' => '/[a-zA-Z]+[a-zA-Z0-9]*/'],
31 ];
32 }
33
34}
35
36$model = new User();
37$model->setScenario('default');
38$model->load($_POST, '');
39if(!$model->validate()) {
40 Yii::error($model->getErrors(),'DATA_VALIDATION');
41 ...
42}
43
44...

rules() 返回数组结果中的每一个数组元素,分别是针对不同的属性在不同的场景下的验证规则,其格式含义如下:

[attributes [] | String, Validator | Closure | String, Validator 子类实例初始化列表]

  • 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] 多个场景中起作用

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

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

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

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

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

复制代码
1 'scenario1' => ['!active attribute1','active attribute2' ]

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

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

总结

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

作者介绍:
杨振振,贝壳找房 SRE PHP 研发工程师,目前负责 HOLONET 项目。

本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。

原文链接:

https://mp.weixin.qq.com/s/N5za6Qod7GzjZe1nd6gnGQ

评论

发布