ThinkPHP关联查询中,with()不支持直接加WHERE条件过滤主表,需用hasWhere()筛选“有关联且满足条件”的主表记录;若要限制关联结果字段或条件,应配合with()闭包、关联定义scope或手动join()。
ThinkPHP 模型关联查询时加 WHERE 条件,不能直接写在
里
ThinkPHP 的
方法默认只做关联预加载,不支持直接传 WHERE 条件。你写
看似合理,但实际在 6.x 中会报错或被忽略——因为
的闭包只控制关联模型的查询范围,不参与主表 JOIN 条件生成;若想筛出“只关联到 status=1 的 user”,必须用
或显式
。
要用
筛选「有关联且满足条件」的数据
这是最常用也最安全的方式,适用于「主表记录必须存在指定条件的关联记录」场景,比如「查所有有启用状态用户的订单」:
本质是生成
子查询,性能比
更可控,尤其数据量大时
它不会改变主表字段结构,避免
可能导致的重复、NULL 字段干扰
注意:
不会把关联字段加载进结果,只是过滤主表;要同时取关联数据,得再补
示例:
想在关联结果里限制字段或加条件,得用
+ 关联定义里的
或闭包
如果你的目标是「查订单,每个订单只加载其用户中 status=1 的那条(哪怕有多个用户)」,这才是
闭包的正确用法。但要注意版本差异:
ThinkPHP 6.0+ 支持在
闭包中调用
,但仅作用于关联模型的 SELECT 查询,不影响主表 JOIN 行为
关联定义(如
方法)里如果用了
/
,闭包中的
是有效的;但
下需配合
防止多条
常见坑:闭包里写了
却没配
,结果返回空数组(MySQL 严格模式下)
示例:
需要字段级只读或动态过滤?别硬套关联,考虑
+ 手动
当业务要求「只读某个字段」(比如只取
,不取
),或者 WHERE 条件涉及多个关联表字段(如
),
和
就不够用了:
立即学习
“
PHP免费学习笔记(深入)
”;
必须和
配合使用,否则字段名会被自动加上表前缀导致 SQL 报错
手动
后,记得用
给关联表起别名,否则
中字段无法区分
性能上,
在大数据量时容易拖慢,建议给关联字段建联合索引(如
)
示例:
真正难的不是写法,而是分清你要的是「过滤主表」还是「过滤关联结果」——前者用
,后者才轮到
闭包;混用或强塞会导致 SQL 语义错乱,查不到数据还找不到原因。
with()with()with(['user' => function ($query) { $query->where('status', 1); }])with()hasWhere()join()hasWhere()hasWhere()EXISTSjoinjoinhasWhere()with()OrderModel::hasWhere('user', ['status' => 1])->with('user')->select()with()scopewith()with()where()user()hasOnebelongsTowhere()hasManylimit(1)order()limit()OrderModel::with(['user' => function ($query) { $query->where('status', 1)->limit(1); }])->select()field()join()user.nameuser.emailuser.status = 1 AND profile.level > 5with()hasWhere()field()join()join()alias()where()join(user_id, status)OrderModel::alias('o')->join('user u', 'o.user_id = u.id AND u.status = 1')->field('o.*, u.name as user_name')->select()hasWhere()with()