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 类,充分利用框架自身的校验器。
浙公网安备 33010602011771号