You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Yii2 ActiveQuery能否结合andWhere与orWhere?如何实现归档订单查询?

如何在OrderQuery中实现archived()方法组合OR与AND条件

嘿,这个场景在Yii开发里太常见了!要实现archived()方法查询已过期或已取消的订单,关键是要把这两个OR条件打包成一个整体,避免和其他可能存在的AND条件逻辑冲突。直接用orWhere会踩优先级的坑,我给你两种靠谱的实现方式:

方式一:直接嵌套OR条件组(简洁直观)

这种写法直接把两个条件用OR数组包裹,传递给andWhere,Yii会自动把这个OR组作为一个整体和其他条件做AND组合,完全符合SQL逻辑:

public function archived() {
    $this->andWhere([
        'OR',
        ['<=', 'order.expiresAt', date("Y-m-d H:i:s")],
        ['NOT', ['order.cancelled' => null]]
    ]);
    return $this;
}

比如当你链式调用$query->where(['user_id' => 123])->archived()时,生成的SQL会是:

WHERE `user_id` = 123 AND (`order`.`expiresAt` <= '2024-05-20 10:00:00' OR NOT (`order`.`cancelled` IS NULL))

完美实现了「用户123的已过期或已取消订单」的需求。

方式二:复用已有方法(维护友好)

如果想复用已经写好的expired()和cancelled()方法,避免重复代码,可以通过实例化新的Query对象获取它们的条件,再组合成OR组:

public function archived() {
    // 获取expired方法的条件
    $expiredCondition = (new self($this->modelClass))->expired()->where;
    // 获取cancelled方法的条件
    $cancelledCondition = (new self($this->modelClass))->cancelled()->where;
    
    // 组合成OR条件组并添加到查询中
    $this->andWhere(['OR', $expiredCondition, $cancelledCondition]);
    
    return $this;
}

这种方式的好处是,后续如果expired()或cancelled()的逻辑修改了,archived()会自动同步更新,不用重复修改两处代码。

❌ 要避开的错误写法

千万别直接这么写:

// 错误!逻辑会出问题
public function archived() {
    return $this->expired()->orWhere(['NOT', ['order.cancelled' => null]]);
}

因为SQL中AND的优先级高于OR,当你有其他前置条件时,比如$query->where(['user_id' => 123])->archived(),生成的SQL会变成:

WHERE `user_id` = 123 AND `order`.`expiresAt` <= 'xxx' OR NOT (`order`.`cancelled` IS NULL)

这会导致「所有已取消的订单,不管归属哪个用户」都被查出来,完全不符合预期!

内容的提问来源于stack exchange,提问作者xtx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:23:11