扩展Yii2的核心验证类 [ 2.0 版本 ]
Yii2的Validator是非常好用的一类方法,它辅助Model层,完成对数据的校验。现在核心的验证器有这么几类:
[[BooleanValidator]] - 要求属性必须为Bool类型
[[CompareValidator]] - 完成两个属性的比较
[[DateValidator]] - 要求属性必须为日期类型
[[EachValidator]] - 要求数组的每个元素必须满足某个条件
[[EmailValidator]] - 要求属性必须为邮件格式
[[ExistValidator]] - 要求该属性必须存在于此模型或者别的模型个某个属性当中
[[StringValidator]] - 要求该属性必须为字符串
[[RangeValidator]] - 要求属性必须在某个范围之内取值
.....
使用起来,它们都有一样的面孔,那就是,在复写的model::rules方法里面增加一条规则:
['country', 'in', 'range' => ['china', 'usa'], 'message' => 'the country is wrong']
这条规则就可以保证country这个属性必须在china和usa之间二选一,否则就会报'the country is wrong'错误。是不是非常的简单?
这个家族的验证器在活动记录ActiveRecord的使用时非常有用,能保证你插入数据库的数据是正确无误的。
已有的校验类虽然很丰富,但是毕竟不能满足我们全部的对数据校验需求,我们希望能扩展已有的验证器。有没有这样一个方法,既能能以这样简单的方式使用,又能方便我们自己定义校验规则?这就是今天要跟大家分享的内容。
直接上代码:
class RegexValidator extends Validator
{
    /**
     * @var string|array 所要采用的验证方法,可以为string,也可以为如果个方法组成的array
     * 所有的方法必须属于RegexValidator
     */
    public $method = null;
    /**
     * @var array 验证的方法列表
     * 方法必须属于RegexValidator
     */
    private $_methodArray = [];
    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->_methodArray = (array)$this->method;
        if(empty($this->_methodArray)){
            throw new InvalidConfigException("Configuration error:no validating method are found!");
        }
        foreach($this->_methodArray  as $method){
            if(!$this->hasMethod($method)){
                throw new InvalidConfigException("Validating method:\"{$method}\" does not exits!");
            }
        }
    }
    /**
     * @inheritdoc
     */
    public function validateAttribute($model, $attribute)
    {
        $value = $model->$attribute;
        //将错误信息转化为数组,数组元素对应[_methodArray]的验证方法
        $this->message = (array)$this->message;
        foreach($this->_methodArray  as $k => $method){
            $ret = call_user_func([$this, $method], $value);
            if($ret === false){
                $error = isset($this->message[$k]) ? $this->message[$k] : Yii::t('yii', '{attribute} is invalid.');
                $this->addError($model, $attribute, $error);
            }
        }
    }
    /**
     * @inheritdoc
     */
    protected function validateValue($value)
    {
        $this->message = (array)$this->message;
        foreach($this->_methodArray as $k => $method){
            $ret = call_user_func([$this, $method], $value);
            if($ret === false){
                $error = isset($this->message[$k]) ? $this->message[$k] : Yii::t('yii', "\"{$value}\" is invalid specified by the validator:". static::className() ."::$method");
                return [$error, []];
            }
        }
        return null;
    }
    /**
     * @inheritdoc
     */
    public function clientValidateAttribute($model, $attribute, $view)
    {
    }   
   //...这里是你的逻辑…… 
   
    /**
     * 由26个大写英文字母组成的字符串
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function uperchars($data = null)
    {
        $_pattern = "/^[A-Z]+$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * 由26个小写写英文字母组成的字符串
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function lowerchars($data = null)
    {
        $_pattern = "/^[a-z]+$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * 由数字和26个英文字母组成的字符串
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function numschars($data = null)
    {
        $_pattern = "/^[A-Za-z0-9]+$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * 手机号码
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function mobile($data = null)
    {
        $_pattern = "/^(0|86|17951)?(13[0-9]|15[012356789]|1[78][0-9]|14[57])[0-9]{8}$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * Email
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function email($data = null)
    {
        $_res = filter_var($data, FILTER_VALIDATE_EMAIL);
        return empty($_res) ? false : true;
    }
    /**
     * 邮编
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function postcode($data = null)
    {
        $_pattern = "/^[1-9]\d{5}(?!\d)$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * 中文
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function zh($data = null)
    {
        $_pattern = "/^[\x{4e00}-\x{9fa5}]+$/u";
        return self::_regex($_pattern, $data);
    }
    /**
     * URL地址
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function url($data = null)
    {
        $_res = filter_var($data, FILTER_VALIDATE_URL);
        return empty($_res) ? false : true;
    }
    /**
     * 身份证
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function identity($data = null)
    {
        $_pattern = "/^(^\d{15}$)|(^\d{17}([0-9]|X)$)$/";
        return self::_regex($_pattern, $data);
    }
    /**
     * IPv4
     * @param $data mixed 数字或者字符串
     * @return bool
     **/
    public static function ip($data = null)
    {
        $_res = filter_var($data, FILTER_VALIDATE_IP);
        return empty($_res) ? false : true;
    }
    /**
     * 匹配正则公共方法
     * @param $pattern string 匹配模式
     * @param $subject string 对象
     * @return bool
     */
    private static function _regex($pattern, $subject = null)
    {
        if ($subject === null)
        {
            return false;
        }
        if (preg_match($pattern, $subject))
        {
            return true;
        }
        return false;
    }
}
首先,必须继承yii\validators\Validator,并且复写validateAttribute和validateValue方法。validateAttribute是验证属性用的,调用$module->validate()时会对其隐式的调用;validateValue则可以不依赖Model独立使用。clientValidateAttribute则是在客户端实现数据校验的部分(这部分等待聪明的你去DIY)。init实现初始化的功能。
复写了基础的几个Validator方法,然后就是我们自己的校验数据的逻辑:
[[zh]] - 校验数据是否为中文
[[postcode]] - 校验数据是否为邮编
[[mobile]] - 校验数据是否为手机号码
……
接下来,当然是最精彩的部分——如何使用?有三种方式可以方便您调用:
1.用在数据模型model的rules方法里面:
['name', RegexValidator::className(), 'method' => 'lowerchars', 'message' => '名字必须全为小写字母']
[
    ['mobile', 'status'],
    RegexValidator::className(),
    'method' => ['mobile', 'zh'],
    'message' => ['手机格式不正确', '必须为中文']
]
这里需要在Model里引入RegexValidator类,并用RegexValidator::className()代替核心验证器'in','string','exsit'等;
规则里的'method'是你自己定义的(静态)方法,你的校验逻辑之所在。可以单个引用,也可以为数组,当为数组时对应的错误信息'message'也得为数组,而且错误信息与之对应。
当调用$model->validate(),如果不满足以上的条件这个方法就会返回false,而且在$model->getErrors()里面会返回具体的错误信息:
[
    'name' => [
        '名字必须全为小写字母',
    ],
    'mobile' => [
        '手机格式不正确',
    ],
    'zh' => [
        '必须为中文',
    ],  
]
返回的错误信息和核心验证器格式是完全一样的。
2.脱离model独立使用,必须要配置[method]参数:
$valid = new RegexValidator([
    'method' => ['zh', 'negative'],
    'message' => ['必须为中文', '必须为负数'],
)];
$valid->validate($value, $error);
if($error){
    echo $error;
}
同样的和核心验证器的使用方法相同;
3.直接调用RegexValidator里的各个静态方法进行验证
$value = 'Abc';
$ret = RegexValidator::mobile($value);
if(!$ret){
	echo ...;
}
这是最简单调用方法,此时无法使用错误提示。
这个类是可以扩展的,您可以将自己的逻辑在[[number]]以降继续添加。
此文为抛砖引入之作,欢迎大家继续在拙作上继续添砖加瓦,使得Yii2数组验证器的功能更加强大!
米粒人生 苏州
最后登录:2021-04-25
在线时长:47小时54分
- 粉丝111
 - 金钱6555
 - 威望230
 - 积分9325
 
热门源码
- 整合完 yii2-rbac+yii2-admin+adminlte 等库的基础开发后台源码
 - 基于 Yii 2 + Bootstrap 3 搭建一套后台管理系统 CMF
 - 适合初学者学习的一款通用的管理后台
 - yii-goaop - 将 goaop 集成到 Yii,在 Yii 中优雅的面向切面编程
 - yii-log-target - 监控系统异常且多渠道发送异常信息通知
 - 店滴云1.3.0
 - 面向对象的一小步:添加 ActiveRecord 的 Scope 功能
 - Yii2 开源商城 FecShop
 - 基于 Yii2 开发的多店铺商城系统,免费开源 + 适合二开
 - leadshop - 基于 Yii2 开发的一款免费开源且支持商业使用的商城管理系统
 
共 5 条评论
前排 分享是大家共同进步的阶梯 献上我的敬意
好的,谢谢!最近在解剖Yii2源代码,以后会分享更多东西!
表示感谢!
一起进步!
谢谢分享。
共同成长!
刚好需要,收藏了,谢谢楼主分享!
文章不错,针对数组的验证yii2本身就一个each而已,但是满足不了大众的需求。那么扩展自己项目的验证需求就非常有必要了。楼主开了个好头。
是的,简单的扩展可以就在我这代码下面继续写;复杂的就重新写一个Validator