CakePHP 4.1.4中查询构建器结合OR条件与预加载的实现问题
解决你的CakePHP查询构建器匹配SQL逻辑问题
首先得明确你原始SQL的逻辑——由于SQL中AND优先级高于OR,你的目标查询等价于:
SELECT * FROM tasks JOIN users ON users.id = tasks.user_id WHERE (tasks.status = 1 AND tasks.account_id = 1393 AND users.authorised = 'yes') OR users.area_id = 1346;
你之前的尝试都没正确还原这个逻辑,导致结果不符,下面是正确的实现方式:
正确写法1:使用表达式构建器(更灵活)
这种方式适合复杂逻辑的场景,可读性更强:
$query = $Tasks->find() ->contain(['Users']) ->where(function ($exp, $q) { // 构建第一个条件组:三个AND关联的条件 $mainConditions = $exp->and([ 'Tasks.status' => 1, 'Tasks.account_id' => 1393, 'Users.authorised' => 'yes' ]); // 用OR连接主条件组和area_id条件 return $exp->or([ $mainConditions, ['Users.area_id' => 1346] ]); });
正确写法2:数组式写法(更简洁)
如果逻辑不复杂,直接用数组嵌套就能快速实现:
$query = $Tasks->find() ->contain(['Users']) ->where([ 'OR' => [ // 第一个OR分支:三个AND条件的组合 [ 'Tasks.status' => 1, 'Tasks.account_id' => 1393, 'Users.authorised' => 'yes' ], // 第二个OR分支:单独的area_id条件 ['Users.area_id' => 1346] ] ]);
为什么之前的尝试失败?
我帮你拆解下每一次尝试的问题:
- 尝试1 & 尝试2:你把
OR条件嵌套在了AND条件组里,生成的SQL逻辑是tasks.status=1 AND tasks.account_id=1393 AND (users.authorised='yes' OR users.area_id=1346),这和你原始SQL的逻辑完全不一样——原始是**(三个AND条件) OR (area_id条件),而你写成了三个AND条件包含OR**。 - 尝试3 & 尝试4:错误地把主表
Tasks的条件放到了contain的闭包里,contain里的条件只会过滤关联的Users数据,不会作用于Tasks主表,这就导致主表的筛选完全失效,自然得不到预期结果。
额外优化:匹配原始SQL的INNER JOIN
默认情况下contain会使用LEFT JOIN,如果你需要和原始SQL完全一致的INNER JOIN,可以显式指定:
// 方式1:用innerJoinWith替代contain(适合不需要加载关联数据,仅筛选的场景) $query = $Tasks->find() ->innerJoinWith('Users') ->where([ 'OR' => [ [ 'Tasks.status' => 1, 'Tasks.account_id' => 1393, 'Users.authorised' => 'yes' ], ['Users.area_id' => 1346] ] ]); // 方式2:在contain里指定连接类型(适合需要加载关联Users数据的场景) $query = $Tasks->find() ->contain(['Users' => function ($q) { return $q->joinType('INNER'); }]) ->where([ 'OR' => [ [ 'Tasks.status' => 1, 'Tasks.account_id' => 1393, 'Users.authorised' => 'yes' ], ['Users.area_id' => 1346] ] ]);
这样就能完全匹配你原始SQL的行为,得到预期的结果集了。
内容的提问来源于stack exchange,提问作者Zenzs
相关产品推荐
相关产品推荐

