闭包筛选必须写在with()第二个参数中,仅控制关联模型查询字段和条件,不影响主表数量,不支持join或引用主表字段,且field()须包含外键和主键。

闭包筛选必须写在 with() 的第二个参数里,不是加在主查询上,也不是塞进关联方法里;写错位置就等于没写。
with() 闭包里能做什么、不能做什么
闭包只控制「关联模型查哪些数据」,不影响主表记录数量,也不参与 JOIN 条件生成。它本质是给关联模型单独构造一个子查询。
- ✅ 支持
where()、field()、limit(1)、order()(但order()不配limit()在严格模式下可能返回空) - ❌ 不支持
join()或嵌套with()(想多层带条件得用嵌套闭包) - ❌ 不能引用主表字段,比如
$query->where('user_id', $this->id)会报错 —— 闭包里没有主模型上下文 - ❌ 写成
with('user')->where('status', 1)完全无效,SQL 里根本没 JOIN user 表,自然找不到 status 字段
字段裁剪必须包含外键和主键
只查 nickname 看似省事,但如果漏了 user_id(外键)或 id(主键),toArray() 里关联数据就是空数组 —— ThinkPHP 绑定失败,不是没查到,是压根连不上。
- 正确写法:
with(['user' => function ($q) { $q->field('id,user_id,nickname,avatar'); }]) - 别名冲突时加前缀:
$q->field('user.id as user_id, nickname')(注意:这里user.id是允许的,因为已在闭包中明确指向关联模型) -
with('user')->field('nickname')无效,field()只作用于主模型
多个闭包条件叠加时 SQL 性能容易失控
每个带闭包的 with() 都会生成独立子查询,比如同时加载「已审核评论数」和「待审评论数」,就会跑两个 (SELECT COUNT(*) FROM ...),而不是一次 JOIN 统计。
- 慎用:
withCount(['comments' => function ($q) { $q->where('status', 1); }], ['comments' => function ($q) { $q->where('status', 0); }])→ 两个子查询 - 更优解:手动
join()+field()+group(),或改用hasWhere()过滤主表后再查总数 - TP6.0.13+ 才稳定支持闭包中
field(),老版本可能静默忽略,建议先getLastSql()确认生成语句
最常被忽略的是:闭包里的 where() 和主查询的 where() 完全隔离,它们各自生效,互不感知。你以为在筛“有关联且满足条件”的主表数据,其实只是把关联结果变少了 —— 主表该出几条还是几条。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











