Model的扩展应用,通过注解进行参数验证

1.基础类,common\service 目录下

<?php

namespace common\service;


use yii\base\Model;
use yii\base\Exception;
use yii\db\ActiveRecord;

/**
 * 基础服务处理类
 * @package common\service
 */
class BaseService extends Model
{
    /**
     * @var array 属性标签
     */
    private $_attributeLabels = [];

    /**
     * @var array 默认的属性规则,如果 static::rules() 没有定义,将启用这里的规则 public 的属性全部为 safe
     */
    private $_rules = [];

    public function init()
    {
        $this->parseAttrDoc();
        parent::init();
    }

    public function rules()
    {
        return $this->_rules;
    }


    public function attributeLabels()
    {
        return $this->_attributeLabels;
    }

    /**
     * 注入 public 属性的注释
     * @use 请用 ‘@var type 属性名‘ 注解标注你的属性定义
     * @use 请用 `@Rule(['string','max'=>10,'min'=>2])` 注解表示这个属性的检验规则
     */
    protected function parseAttrDoc()
    {
        $reflectionClass = new \ReflectionClass(static::className());

        $function = function ($ruleParams) {
            $rules = [];
            foreach ($ruleParams as $ruleParam) {
                $data = @eval('return ' . $ruleParam . ';');
                if ($data) {
                    $rules[] = $data;
                }
            }
            return $rules;
        };

        foreach ($reflectionClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
            if ($property->isStatic()) {
                continue;
            }

            $name = $docName = $property->getName();
            $doc  = $property->getDocComment();
            if (empty($doc)) {
                $this->_rules[]                = [[$name], 'safe'];
                $this->_attributeLabels[$name] = $docName;
                continue;
            }

            preg_match('/@var\040+\w+\040+([^\n\040]+)/', $doc, $matches)
            && !empty($matches[1])
            && ($docName = $matches[1]);
            $this->_attributeLabels[$name] = $docName;

            $isRequired = !isset($this->{$name});//如果没有设默认值,那我默认你需要它 required
            $isSafe     = !$isRequired;

            if (
                preg_match_all('/@Rule\(([\w\W]+?)\)/', $doc, $matches)
                && !empty($rules = call_user_func($function, $matches[1]))
            ) {
                /**
                 * !empty($rules = call_user_func($function, $matches[1]))

                 可以替换成下面的代码,
                $rules = [];
                $matchRule = $matches[1][0];
                $rules = eval("\$rules=$matchRule;");
                 */
                foreach ($rules as $rule) {
                    $type           = array_shift($rule);
                    $this->_rules[] = [[$name], $type] + $rule;
                    if ($type = 'required') {
                        $isRequired = false;
                    } else {
                        $isSafe = false;
                    }
                }
            }

            if ($isRequired) {
                $this->_rules[] = [[$name], 'required'];
            } elseif ($isSafe) {
                $this->_rules[] = [[$name], 'safe'];
            }

        }
    }

    public function loadGet($formName = '')
    {
        $this->loadParams(false,$formName);
    }

    public function loadPost()
    {
        $this->loadParams();
    }

    /**
     * 一个简单的载入参数,加了校验
     * @param bool $is_post
     * @param string $formName
     * @return string
     */
    public function loadParams($post = true, $formName = '')
    {
        $params = $post ? \Yii::$app->request->post() : \Yii::$app->request->get();
        $this->load($params, $formName);
        if (!$this->validate()) {
            return $this->getErrorMessage();
        }
        return '';
    }

    /**
     * 在事务中的 Model::save()
     * @param ActiveRecord $model
     * @throws ModelException
     */
    public function modelSaveAndJudage(ActiveRecord $model, $runValidation = true)
    {
        if (!$model->save($runValidation)) {
            $message = $this->getErrorMessage($model);
            \Yii::error($model->className() . ":" . $message);
            throw new ModelException($message);
        }
    }




    public function judgeException()
    {
        if ($message = $this->getErrorMessage()) {
            throw new ModelException($message);
        }
        return true;
    }

    /**
     * @param ActiveRecord|string $class
     * @param $condition
     * @return ActiveRecord
     */
    public static function findOneOrNew(string $class, $condition):ActiveRecord
    {
        $model = $class::findOne($condition);
        if(empty($model)){
            if(!is_array($condition)){
                $key = $class::primaryKey()[0];
                $condition = [
                    (string)$key => $condition
                ];
            }
            /** @var ActiveRecord $model */
            $model = new $class($condition);
            $model->loadDefaultValues();
            foreach ($condition as $key => $item) {
                $model->{$key} = $item;
            }
        }
        return $model;
    }

    public function getErrorMessage(Model $model = null)
    {
        if (empty($model)) {
            $model = $this;
        }
        $message = '';
        $error   = $model->getFirstErrors();
        if ($error) {
            $message = array_pop($error);
        }
        return $message;
    }

    /**
     * 获取所有的错误信息
     * @return string
     */
    public function getErrorsToString(Model $model = null)
    {
        if (empty($model)) {
            $model = $this;
        }

        $errors = [];
        foreach ($model->getErrors() as $field => $error) {
            foreach ($error as $item) {
                //$errors[] = $field . '->' . $item;
                $errors[] = $item;
            }
        }
        if (empty($errors)) return '';
        return implode('|', $errors);
    }

    /**
     * 获取第一个错误信息
     * @return string
     */
    public function getFirstErrorsToString(Model $model = null)
    {
        if (empty($model)) {
            $model = $this;
        }

        return (string)@array_shift($model->getErrors())[0];
    }
}

class ModelException extends Exception
{
    public function getName()
    {
        return self::class;
    }

}

  

2. 应用项目创建一个基类,继承上面的基础类,做一个中间的BaseModel 类,是为了每个独立的应用,都可以重写自己的方法。

虽然是空类,但是为了后期每个应用的扩展,也要写这一层。

说明:基础类 (全局) -> 基类(应用) -> 应用的调用

 

假如,我们在 admin 下应用

<?php

namespace admin\models;


use common\service\BaseService;

class BaseModel extends BaseService
{

}

  

3. 创建业务Model,我们假如做一个发布文章的表单。

<?php

namespace admin\forms\Posts;

use admin\models\BaseModel;


class PostModel extends BaseModel
{


    /**
     * @var string 标题
     * @Rule(["string","title"=>500])
     */
    public $remark = '';

    /**
     * @var int 分类ID
     */
    public $catId;

    /**
     * @var string 来源
     * @Rule(['safe'])
     */
    public $from = null;
	
	//是否写日志
	$public $doLog = true;

    

    /** @var CategoryModel 分类模型*/
    protected $category = null;


	public function init()
    {
	   //初始化其他的数据,赋值给 private 的类变量,方便后期调用

        parent::init(); // TODO: Change the autogenerated stub
    }
	

    public function validate($attributeNames = null, $clearErrors = true)
    {
        if (!parent::validate($attributeNames, $clearErrors)) {
            return false;
        }
		
		$category = '根据ID获取分类对象';
        
        if(empty($this->category)){
            $this->addError('分类校验', '找不到对象');
        }
        return !$this->hasErrors();
    }



    public function save()
    {

        $transaction = \Yii::$app->getDb()->beginTransaction();
        try {
            //业务逻辑处理
            $this->doLog && $this->afterOperate($fee, $begin_date);
            $transaction->commit();
        } catch (\Exception $exception) {
            $this->addError('db',$exception->getMessage());
            $transaction->rollBack();
            return false;
        }
        return true;

    }

    public function afterOperate()
    {

    }
}

  

4. 控制器代码如下:

public function actionPost()
{
	$service = new PostModel();
	if ($service->mLoad() && $service->save()) {
		echo '发布成功';
		return;
	} 
	
	echo '发布失败,原因:' . $service->getErrorMessage();
}

 

控制器就短短几句代码,就可以。

 

总结:在做数据提交,获取其他业务逻辑时,尽量使用Model 类,充分利用框架自身的校验器。

 

posted on 2019-04-24 01:28  追风的浪子  阅读(203)  评论(0)    收藏  举报

导航