Yii2 joinWith left join 附加 on 额外 and 条件
Yii2 joinWith left join 附加 on 额外 and 条件
joinWith默认把 closure 里面的条件放到 WHERE,不是 ON。想要LEFT JOIN xxx ON a=b AND c=d,必须用onCondition,不能写andFilterWhere / andWhere。
关键点
joinWith('关联名', false):第二个参数false,不执行 eager loading 预加载,只做 JOIN SQL;true 会额外做一次 IN 查询。onCondition里面写的条件,会拼接在ON后面,生成AND xxx=xxx。andWhere/andFilterWhere会跑到WHERE子句,会把 left join 变成等价 inner join,这是高频踩坑。
模型关联定义(必须先定义 relation)
// Order 模型
public function getLog()
{
/**
* LEFT JOIN log ON log.order_id = order.id AND log.type = 1
*/
return $this->hasMany(Log::class, ['order_id' => 'id'])
->onCondition(['log.type' => 1]); // 这里的条件直接拼 ON AND
}如果条件需要动态变量,用数组格式防注入:
->onCondition([
'log.type' => $typeVal,
'log.status' => 2
]);查询使用 joinWith
$query = Order::find()
->joinWith([
'log' => function($q) {
// ❌ 不要在这里写 andWhere,会进 WHERE
// ✅ 如果需要再追加 ON 的 AND,继续 onCondition
$q->onCondition(['log.is_del' => 0]);
}
], false) // false:只JOIN,不贪婪加载
->all();生成 SQL:
SELECT `order`.* FROM `order`
LEFT JOIN `log` ON `log`.`order_id` = `order`.`id` AND `log`.`type`=1 AND `log`.`is_del`=0这里说一下 joinWith 第二个参数 $eagerLoading(bool)
方法签名:
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')$eagerLoading = false:只执行 SQL JOIN,不做贪婪(eager)预加载。
$eagerLoading = true(默认)
Order::find()->joinWith('log', true);会执行2条SQL:
- 主查询:
SELECT order.* LEFT JOIN log ON ...拿到主表+关联表连接后的数据集 - 额外再跑一条 IN 查询:
SELECT * FROM log WHERE order_id IN (1,2,3...),把关联模型数据查出来,给模型的$order->log属性赋值。
目的:做贪婪加载,循环遍历$orders的时候,访问$order->log不会触发数据库查询。
代价:多一次数据库查询。即使你根本不需要读取$order->log,这条SQL依旧会执行。
$eagerLoading = false
Order::find()->joinWith('log', false);只执行1条SQL:带LEFT JOIN的主查询。
- SQL里会做
LEFT JOIN,你可以在select里取log表字段、可以where过滤log表字段; - 不会额外执行IN查询,不会填充模型关联属性。
- 此时你访问
$order->log,会触发延迟加载(lazy load),单独再查一次数据库。
使用场景什么时候写 false
仅仅为了做表连接,用于过滤、排序、取关联表字段,业务代码不读取
$model->relation绝大多数报表、列表查询场景,都用
false,节省一次SQL。// 用于 where、orderBy、select取log.*,不需要读取$order->log Order::find() ->joinWith('log',false) ->select('order.*,log.content') ->andWhere(['log.type'=>1]) ->orderBy('log.create_time desc');- 你只是需要
LEFT JOIN xxx ON ... AND ...,不需要拿到关联模型对象。
什么时候用 true(默认)
需要循环遍历结果,并且要读取关联模型属性:
$list = Order::find()->joinWith('log',true)->all();
foreach($list as $item){
echo $item->log[0]->content; // 不会N+1,全部已经预加载完毕
}场景2:不想修改模型关联,查询时临时追加 ON AND
模型本身不带 onCondition,查询动态增加 ON 条件
$query = Order::find()
->joinWith([
'log' => function ($q) {
/** @var $q ActiveQuery */
// 追加 ON AND 条件,不会污染模型定义
$q->onCondition([
'log.type' => 1,
'log.is_del' => 0
]);
}
], false)
->all();场景3:onCondition 需要大于、不等于这类非等于条件
onCondition 支持 yii 的条件数组格式:
$q->onCondition([
'and',
['log.type' => 1],
['>', 'log.create_time', '2026-01-01 00:00:00']
]);输出:ON ... AND log.type=1 AND log.create_time > '2026‑01‑01 00:00:00'
场景4:原生 SQL 习惯,直接手写 join(备选)
如果逻辑复杂,也可以直接 leftJoin,完全手写on:
Order::find()
->leftJoin('log', 'log.order_id = `order`.id AND log.type = :t', [':t' => 1])
->all();常见错误对比
❌ 错误写法(条件跑到 WHERE,left join 失效)
->joinWith(['log' => function($q){
$q->andWhere(['log.type' =>1]); // 条件进 WHERE,不是 ON
}], false);✅ 正确:onCondition,条件拼接在 ON ... AND xxx
小提示
joinWith(relName, false):只做表连接,不查询关联数据;如果需要同时取出关联模型数据,第二个参数改为true。onCondition是 ActiveQuery 的方法,只作用于 JOIN 的 ON,不会影响 where。- 如果使用
andFilterWhere,同样会跑到 WHERE,绝对不要拿来写 ON 的 and 条件。
版权属于:Joyber
本文链接:https://blog.qqvbc.com/default/Yii2-joinWith-left-join-on-and.html
转载时须注明出处及本声明