Yii2.0 动态模型验证改造,使其支持自定义属性标签

【项目背景】

在提供业务API或者提供业务服务类操作时,往往需要对很多入口参数进行验证。这个时候Yii2.0框架的动态model验证起到了很好的支撑的作用。但是很多参数验证实际同数据库model保存验证类似,希望能够提取到模型的attributeLabels()属性标签进行验证提示。而不是每个字段验证都要定义message提示信息。

 

【改造步骤】

1、继承覆写yii\base\DynamicModel动态模型中的validateData()方法:

<?php

namespace common\ext;

use yii\base\DynamicModel;
use yii\base\InvalidConfigException;
use yii\base\UnknownPropertyException;
use yii\helpers\ArrayHelper;
use yii\validators\Validator;

/**
 * 拓展动态验证model类,使其支持attributeLabels()
 *
 * Class ValidatorModelExt
 * @package common\ext
 */
class ValidatorModelExt extends DynamicModel
{
    /**
     * 属性标签
     *
     * @var array
     */
    private $_attributeLabels = [];

    /**
     * {@inheritdoc}
     */
    public function __get($name)
    {
        try {
            if (strpos($name, '.') !== false) {
                list($prefix, $key) = explode('.', $name, 2);
                if ($this->hasAttribute($prefix)) {
                    $array = parent::__get($prefix);
                    if (!is_array($array)) {
                        return null;
                    }
                    return ArrayHelper::getValue($array, $key);
                } else {
                    return parent::__get($name);
                }
            } else {
                return parent::__get($name);
            }
        } catch (UnknownPropertyException $e) {
            return null;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function __set($name, $value)
    {
        if (strpos($name, '.') !== false) {
            list($prefix, $key) = explode('.', $name, 2);
            if ($this->hasAttribute($prefix)) {
                $array = parent::__get($prefix);
                if (!is_array($array)) {
                    throw new UnknownPropertyException('Setting non array property: ' . get_class($this) . '::' . $name);
                }
                ArrayHelper::setValue($array, $key, $value);
                parent::__set($prefix, $array);
            } else {
                parent::__set($name, $value);
            }
        } else {
            parent::__set($name, $value);
        }
    }

    /**
     * 覆写validateData() method
     *
     * @param array $data the data (name-value pairs) to be validated
     * @param array $rules the validation rules. Please refer to [[Model::rules()]] on the format of this parameter.
     * @param array $attributeLabels attribute label desc
     * @return static the model instance that contains the data being validated
     * @throws InvalidConfigException if a validation rule is not specified correctly.
     */
    public static function validateData(array $data, $rules = [], $attributeLabels = [])
    {
        /* @var $model DynamicModel */
        $model = new static($data);
        if (!empty($rules)) {
            $validators = $model->getValidators();
            foreach ($rules as $rule) {
                if ($rule instanceof Validator) {
                    $validators->append($rule);
                } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
                    // 支持modelClassName对象自动获取attributeLabels属性标签
                    if (!empty($rule['modelClassName'])) {
                        $modelObj = \Yii::createObject($rule['modelClassName']);
                        if (!empty($modelObj->attributeLabels())) {
                            $attributeLabels = array_merge($attributeLabels, $modelObj->attributeLabels());
                        }
                    }
                    unset($rule['modelClassName']); // 删除modelClassName避免校验器报错
                    $validator = Validator::createValidator($rule[1], $model, (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.');
                }
            }
        }
        $model->_attributeLabels = $attributeLabels; // 属性标签赋值

        $model->validate();

        return $model;
    }

    /**
     * 获取属性标签
     *
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return $this->_attributeLabels;
    }
}

2、增加ValidatorHelper辅助类为项目提供统一的验证操作(注意:validate()验证方法中使用了上面的ValidatorModelExt拓展类):

<?php

namespace common\helpers;

use common\ext\ValidatorModelExt;
use ts\php\validator\ValidatorModel;
use yii\base\UserException;

/**
 * 验证器helper类
 *
 * Class ValidatorHelper
 * @package common\helpers
 */
class ValidatorHelper
{
    /**
     * 动态rules校验,rules请参考Yii2.0 model rules的定义规则
     *
     * @param string|array $data 需要检验的数据
     * @param array $rules 定义的rules规则
     * @param array $attributeLabels 属性标签描述 注意:也支持在rules中自定义modelClassName参数自动获取model标签属性
     *
     * example:
     *  $rules = [
     *      [['user_name', 'real_name'], 'required', 'modelClassName' => User::className()],
     *  ];
     *
     * @return bool
     * @throws \Exception
     * @throws \yii\base\InvalidConfigException
     */
    public static function validate($data, $rules = [], $attributeLabels = [])
    {
        if (empty($rules)) {
            return true;
        }

        $model = ValidatorModelExt::validateData($data, $rules, $attributeLabels);
        if ($model->hasErrors()) {
            $error = $model->getErrorSummary(false);
            throw new \Exception(reset($error));
        }

        return true;
    }
}

【如何使用】

温馨提示:以下两种方式也可以结合使用哦!

方式1、自定义attributeLabels属性标签,然后调用ValidatorHelper::validate()方法,如下图所示:

 

 方式2、在rules规则中定义modelClassName属性,则会自动提取对应模型类attributeLabels()定义的属性标签,如下图所示:

 

 

 

posted @ 2020-02-08 12:53  冰狼爱魔  阅读(545)  评论(0编辑  收藏  举报