面向对象的一小步:添加ActiveRecord的Scope功能_蜘蛛资讯网 七码阶梯式倍投方案
您现在的位置:蜘蛛资讯网将夜得利斯
作者:百年不渡??分类:未来幻想??点击:805173次??下载:6225732次??大小:70M??日期:2019-09-23

安顺越壕奖企业管理有限公司_面向对象的一小步:添加ActiveRecord的Scope功能

问题场景

我们用Yii2的ActiveRecord功能非常的方便,假如我们有个Model叫Student,那么ActiveQuery可以通过这种方式轻便地获得:

$query = Student::find();

然后,我们就可以在$query上继续使用各种方法添加SQL Clause:

$query->where(["gender" => "male" ]); //选择男生

$query->where([">",  age" => "18" ]); //选择年龄大于18的

如果这条学生的记录需要审核,还可能有

$query->where([""check_status" => "1" ]);  // 1-审核通过

最后,使用all()或者one()获取结果。

query gt where"" check_status" gt " 1" 1 shen he tong guo zui hou, shi yong all huo zhe one huo qu jie guo.

这是使用Yii2的小伙伴们天天在做的事情。

但是,笔者有时也觉得动不动就写一长串的where条件挺讨厌的,一则是太长,二则是可阅读性也不大好,["check_status" => "1" ]这类的条件有时并不容易看出是何种意图。所以得找一种更为简便的方法就变得很有必要。

优化

写出面向对象化的代码是笔者的追求,我们希望能这样的去做查询:

Student::male()->all();  //选择男生

Student::checked()->male()->all();  //选择审核通过的男生

看不到那么多长长的where语句,就像伪代码一样良好的可读性,就算没用用过Yii2的小伙伴也能看懂是啥意思。如果能能这样去写代码,你愿不愿意?

实现

要说到如何实现,那就得依赖强大的魔术方法__call()和__callStatic()了。我们直接抛出代码,让大家看看是怎么实现的。

定义Scope方法

首先,需要在Model内部,定义一些Scope方法,用以封装特定的where条件集合,表示相对常用筛选条件。
Scope方法的格式为

public function xxxScope($param1, $param2,..., yiidbActiveQuery $query)
{
  return $query->where(["attr1" => $param1])->where(["attr2" => $param2]);
}

注意,xxxScope方法的前面的$param1, $param2,...都是可选的,最后一个参数一定是AQ的一个实例$query,用以接收where条件,最后需要return将其返回。

扩展ActiveRecord

要实现Student::checked(),Student::male()这类方法,使得其也能得到AQ实例,那就得重载ActiveRecord的find()静态方法;同时,为了使::male()和::gender()这类静态方法得以实现,还实现了__callStatic()方法:

getParameters();
            array_pop($params); // 先将$query pop出
            $newArgs = [];
            foreach ($params as $k => $param) { //对除$query外形参进行遍历
                if (isset($arguments[$k])) {
                    $newArgs[$k] = $arguments[$k];
                } else { //实参数小于形参数,传参不够的情况
                    if ($param->isDefaultValueAvailable()) {//有默认值就取默认值
                        $newArgs[$k] = $param->getDefaultValue();
                    } else {
                        $newArgs[$k] = null; //无默认值设为null
                    }
                }
            }
            $newArgs[] = $static::find(); //将static::find()作为最后一个参数

            return call_user_func_array([$static, $scopeMethod], $newArgs);  //调用Scope方法
        }
        throw new yiiaseInvalidCallException("Method: $name not found!");
    }
    
}

上面使用了ReflectionMethod反射类来分析Scope方法的参数列表,是为了避免在使用Scope方法时因为传参数量不对而导致的错误。例如,我们在Student中定义了一个status的Scope方法,参数为一个$status

public function statusScope($status = 1, ActiveQuery $query)
{
    return $query->where(["check_status" => $status]);
}

我们在使用的时候如果不小心这样使用:
Student()::status(1, 2)->all()
那么statusScope方法自动过滤多余传参,保证最后一个参数为一个ActiveQuery。

扩展ActiveQuery

上面提到的BaseActiveQuery是扩展自ActiveQuery,重载了__call()方法,使得->male(),->checked()访问得以实现:

class BaseActiveQuery extends yiidbActiveQuery
{
    public function __call($name, $arguments)
    {
        $scopeMethod = $name."Scope";

        //检查Scope方法是否存在
        if (method_exists($this->modelClass, $scopeMethod)) {
            //用ReflectionMethod分析Scope方法分析参数列表
            $method = new ReflectionMethod($this->modelClass, $scopeMethod);
            $params = $method->getParameters();
            array_pop($params); // 先将$query pop出

            $newArgs = [];
            foreach ($params as $k => $param) {  
                if (isset($arguments[$k])) {
                    $newArgs[$k] = $arguments[$k];
                } else {                
                    if ($param->isDefaultValueAvailable()) {
                        $newArgs[$k] = $param->getDefaultValue(); /
                    } else { 
                        $newArgs[$k] = null; 
                    }
                }
            }
            $newArgs[] = $this;//将自身作为形参最后一个参数

            return call_user_func_array([$this->modelClass, $scopeMethod], $newArgs);
        }

        //最后的关键一步:别忘了调用父方法的__call
        return parent::__call($name, $arguments);
    }
    
}

另外,如果你喜欢,还可以在BaseActiveQuery加上另外两个常用的方法,用来转化为数组:

public function get()
{
    return $this->asArray()->all();
}

public function first()
{
    return $this->asArray()->one();
}

BaseActiveRecord和BaseActiveQuery都可以放在自己的一个公共目录下,例如

common/base/BaseActiveRecord.php
common/base/BaseActiveQuery.php

使用

实现了前面几步,我们就可以愉快的玩耍了。
Scope方法可以作为静态方法被AR调用,也可以作为非静态方法被AQ调用,同时支持链式操作,灵活性非常大。

Student::male()->checked()->age(20)->all();
Student::age(20)->checked()->get();
Student::find()->checked()->where(["is_deleted" => "0"])->male()->all();
Student::checked()->where(["like", "name", "Jason"])->female()->first();
.....

这样的查询是不是更清晰,更友好?

进一步优化

PHP是世界上最好的语言——这句话一直争议不断,然而PHPStorm是PHP最好的编辑器却似乎越来越没有争议。因此为了PHPStorm能更好的追踪代码,还需要做小小的优化。
在AR(如Student)头部DOC部分添加:

* @method BaseActiveQuery checked()
* @method BaseActiveQuery male()
* @method BaseActiveQuery age()

恭喜Yii2进一步向面向对象化又迈出了坚实的一小步!

当前文章:http://www.iplayhq.com/yq7/280900-540801-87859.html

发布时间:05:41:48

4887铁算盘??白小姐论坛??www.780111.com??447448.com??999151.com??679922.com??开奖网址kjcc??香港正版挂牌彩图每期??香港本港台现场开奖??香港财神爷心水论坛??

您可能还对以下电子书感兴趣

安顺越壕奖企业管理有限公司TXT下载声明:

1 蜘蛛资讯网免费提供的安顺越壕奖企业管理有限公司,均由网友上传,供下载测试之用,不作商业用途,下载后请二十四小时后删除!

2 我们根据txt小说全文所整理出安顺越壕奖企业管理有限公司txt电子书全集免费下载,由程序自动生成安顺越壕奖企业管理有限公司txt下载文件。

3 书友所发表的txt小说安顺越壕奖企业管理有限公司的相关评论,并不代表本站赞同安顺越壕奖企业管理有限公司txt下载或者支持安顺越壕奖企业管理有限公司的读者观点。

4 如果发现小说《安顺越壕奖企业管理有限公司txt全集》无法下载未及时更新请联系我们。如果您喜欢安顺越壕奖企业管理有限公司txt电子书,请支持作者到书店购买正版图书。感谢您的合作与支持。

5 好看的小说安顺越壕奖企业管理有限公司是作者"百年不渡"的最新力作,安顺越壕奖企业管理有限公司电子书由网友发布;小说安顺越壕奖企业管理有限公司版权属于作者所有,如果侵犯您的利益,请通知我们。