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

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

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

样例

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


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子类实例初始化列表]

  • 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)数组。

  [
      'scenario1' => ['active attribute1','active attribute2' ]
      'scenario2' => ['active attribute1','active attribute2' ]
      'scenario3' => ['active attribute1','active attribute2' ]
  ]

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

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

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

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

总结

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