米粒人生 2018-08-01 10:08:05 4177次浏览 3条评论 5 1 0

前段时间发表了一篇文章 面向对象的一小步:添加 ActiveRecord 的 Scope 功能 提到一种更加友好的方式做数据库查询。经小伙伴的建议,在满足同样条件下,可以有更为简洁的封装方法。

这需要用到重载ActiveQuery方法,在通过gii建数据模型model时,可以勾选"Generate ActiveQuery"这么一项自动生成。

例如,在生成Student Model时,可以自动生成StudentQuery

class StudentsQuery extends \yii\db\ActiveQuery
{
    /**
     * {@inheritdoc}
     *
     * @return TbStudents[]|array
     */
    public function all($db = null)
    {
        return parent::all($db);
    }

    /**
     * {@inheritdoc}
     *
     * @return TbStudents|array|null
     */
    public function one($db = null)
    {
        return parent::one($db);
    }

    //默认筛选已经审核通过的记录,当然,也可以自己定义$status
    public function checked($status = 1)
    {
        return $this->where(['check_status' => $status]);
    }
}

checked()方法里面有一条where条件,也可以添加多条。总之,这个checked方法可以随你定义, 封装一类现实应用常用的条件组合。

这样,我们便可以这样来查询:

Student::find()->checked()->where(...)->all();

那又如何满足checked方法的静态调用呢?

Student::checked()->where(...)->all();

这还得在Model里面重载 __callStatic() 方法。这个重载可写在一个如BaseModel的公共方法里面,以便大家调用。

public static function __callStatic($name, $arguments)
{
	return static::find()->$name(...$arguments);
}

也更为简洁,同时也是一种透明操作。在StudentQuery中没有定义对应方法或者传参错误都会导致报错。

那么这是如何做到的?
因为在Student里面有这么一段:

public static function find()
{
	return new StudentQuery(get_called_class());
}

可见在 __callStatic 中返回的 static::find() 其实就是一个StudentQuery的一个实例,然后在这个实例中去寻找checked方法。绕了个小圈子,重新回到了StudentQuery。道理非常简单。

现在我们同样可以实现两种友好的查询了:

Student::find()->checked()->where(...)->all();
Student::checked(2)->where(...)->all()

对代码简洁性和透明性的要求比较高的小伙伴,可以采用这种封装。

觉得很赞
您需要登录后才可以评论。登录 | 立即注册