米粒人生 2018-08-06 12:45:25 403次浏览 3条回复 1 2 0

首先抛出一个问题:yii\db\Query中where()方法往往用做第一个条件,后面继续用连接条件要使用andWhere/orWhere/.... 而链式操作->where()->where()会导致后者覆盖前者,所以我们在使用where()时往往要很小心,这样做方便吗?

其实我认为很不方便,因为有时在用where()的时候需要预防前面是否也用过where()方法,用过的话则改用andWhere()。当然,我们也可以一直用andWhere代替where来使用,但这样并不十分方便,我们毕竟用where时居多。

先看看Yii2的源码:

yii\db\Query中:

public function where($condition, $params = [])
{
    $this->where = $condition;
    $this->addParams($params);
    return $this;
}

public function andWhere($condition, $params = [])
{	
    if ($this->where === null) {
      $this->where = $condition;
    } else {
      $this->where = ['and', $this->where, $condition];
    }
    $this->addParams($params);
    return $this;
}

差别非常明显,where显然就当做一个SQL的Where Clause中第一个条件,而且显然不支持链式操作,两个where()链接一起不是两个条件相与,而是后者覆盖前者。相比andWhere则功能更为强大,功能兼容了前者,可以完全替换where()来使用。

窃以为,这是Yii2设计的不太好的一个方面。干嘛不把where()的功能弄的再强大点?

框架代码当然不能随便改动,但是我们可以通过重载做到这点。在Gii创建模型(如Student)时,生成模型自己的Active Query,而让这个Active Query继承一个基类BaseActiveQuery. 在BaseActiveQuery中重载where()方法:

class BaseActiveQuery extends yii\db\ActiveQuery
{
    /**
     * where级联的效果等同于andWhere(),不再区分是否是第一个where条件.
     *
     * @param array|string $condition
     * @param array        $params
     *
     * @return $this
     */
    public function where($condition, $params = [])
    {
        if (null === $this->where) {
            $this->where = $condition;
        } else {
            $this->where = ['and', $this->where, $condition];
        }
        $this->addParams($params);

        return $this;
    }
}

就是将andWhere的逻辑完全搬过来。 后面,例如在StudentQuery中,自己定义where条件就简单多了:

public function active()
{
  $this->where('[[status]]=1');
  return $this;
}

同样,级联条件也简单不少:

Studend::find()->where(['age' => 18])->active()->where(...)->orWhere()..->all();

再者,当使用joinWith进行联表查询时,也更加放心:

Teacher::find()->where(...)->joinWith('student' => function($query) {
  $query->active()->where(...);
})

大家觉得这样是不是会更方便一点?

觉得很赞
  • 回复于 2018-08-07 09:51

    Yii 其中一个特点是高度可定制,任何一个核心类都可以经过自定义,变得适合自己的项目或操作习惯。例如你觉得默认的 where() 不好用,那就自己改一下,让它用着更加顺手。

    这是Yii2设计的不太好的一个方面。干嘛不把where()的功能弄的再强大点

    我认为这见仁见智吧。如前所述,Yii 已经提供了让核心类“变强大”的方式,像 where() 这种使用频率高、基础的方法,更多考虑的是如何设计得足够通用,就像《迷你世界》里面的石块一样,使用同
    一个的石块,不同的玩家可以发挥想象力,盖出各种漂亮的建筑。

    最后简单一个我日常使用 ActiveQuery 的习惯,抛砖引玉,仅供参考:

    常用的查询条件尽可能在 AcriveQuery 内以方法的形式定义

    以楼主举的学生、老师模型为例,可以在 StudtentQuery 内定义如下常用方法:

    public function youngerThan($age)
    {
        return $this->andWhere(['<', '{{%student}}.age', $age]);
    }
    public function male()
    {
        return $this->andWhere(['{{%student}}.sex' => 1]);
    }
    public function toughtBy($teacherIds)
    {
        return $this->andWhere(['{{%teacher}}.id' => $teacherIds]);
    }
    

    方法只要定义得恰当,构建 AcriveQuery 时可以少用甚至不用 where() , andWhere() 或 orWhere(). 例如,我想查一下所有年龄小于 18 岁、同时被张三和李四两位老师教的男同学,就可以这样写:

    $students = Student::find()->joinWith(['teacher'])->youngerThan(18)->taughtBy([3, 5])->male()->all();
    
    
    1 条回复
    回复于 2018-08-10 10:12

    这个AcriveQuery 的使用发放在哪里能找到, 只有英文的看不懂啊。xx

  • 回复于 2018-08-07 10:03

    很同意你的观点。尤其是你在ActiveQuery中对查询条件做封装,很推崇这一点。我用过Laravel的Eloquent ORM里面的where,觉得很好用些,所以才有此疑问。

    Yii 其中一个特点是高度可定制,任何一个核心类都可以经过自定义,变得适合自己的项目或操作习惯——这一点我也是高度认同,也是我最欣赏Yii2设计哲学的原因

    觉得很赞
  • 回复于 2018-08-14 16:23

    有种东西叫做 语义化

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