with() 中的 where 仅过滤预加载数据,不改变主查询结果;需用 whereHas() 筛选主模型,再配合 with() 预加载符合条件的关联数据,才能实现带条件的关联筛选。

with() 里不能直接写 where 条件
很多人试过 with(['posts' => function ($q) { $q->where('status', 'published'); }]),发现查出来的 posts 是对的,但父模型(比如 User)数量没变——这说明 Laravel 默认只做关联数据过滤,不改变主查询结果。这不是 bug,是设计如此:预加载本身不参与主表筛选。
常见错误现象:with() 加了 where 后页面还是显示所有用户,哪怕某个用户根本没满足条件的 posts;或者分页总数不准,因为 with() 不影响 count() 查询。
- 真正想“只查有已发布文章的用户”,得用
whereHas()配合with() -
with()的闭包只控制子查询的 SELECT 部分,不影响 JOIN 或 WHERE 主逻辑 - Laravel 9+ 支持
whereRelation()替代部分whereHas()场景,更轻量
whereHas() + with() 才能实现带条件的关联筛选
这是最常用也最稳妥的做法:先用 whereHas() 筛主模型,再用 with() 预加载符合条件的关联数据。两者配合,语义清晰、SQL 可控、分页准确。
使用场景:后台列表页要显示“有审核通过评论的订单”,且需同时展示这些评论内容。
Order::whereHas('comments', function ($q) {
$q->where('status', 'approved');
})->with(['comments' => function ($q) {
$q->where('status', 'approved');
}])->get();
-
whereHas()生成 EXISTS 子查询,影响主表结果集 -
with()里的闭包会生成独立的 IN 查询或 JOIN,只过滤预加载的数据 - 两个闭包里的条件必须一致,否则可能查出空关联(比如主表筛选了 status=approved,但预加载忘了加,就加载了全部 comments)
- 注意 N+1 风险:如果闭包里用了
orderBy或limit,Laravel 8.30+ 之后才支持,旧版本会忽略
withCount() 和 whereRelation() 适合轻量条件判断
如果只是需要“统计”或“存在性判断”,不用加载完整关联数据,withCount() 和 whereRelation() 更高效,生成的 SQL 更简洁,也避免大字段拖慢查询。
性能影响:相比 with(),withCount() 只查 COUNT(*),whereRelation() 走 JOIN + WHERE,比 EXISTS 略快(尤其在索引良好时)。
-
whereRelation('posts', 'status', 'published')等价于简单 EXISTS,但语法更直白 -
withCount(['posts as published_posts_count' => fn ($q) => $q->where('status', 'published')])可以直接在模型上拿到数字字段 - 它们都不支持复杂子查询(比如嵌套 orWhere),遇到就得退回到
whereHas() - MySQL 5.7+ 和 PostgreSQL 均支持,SQLite 需要 3.25+ 版本
关联预加载条件容易漏掉的边界点
实际写多了就会发现,问题往往不出在语法,而在关联定义本身和数据库状态。最容易被跳过的其实是模型关系方法里的默认约束或软删除设置。
比如 posts() 关系里写了 ->where('deleted_at', null),那你在 with() 闭包里再加 where('status', 'published'),最终 SQL 是 AND 关系——但如果你没意识到这个隐含条件,调试时会以为“为什么 status 条件没生效?”
- 检查关联方法源码,确认有没有默认
where、withTrashed()或withoutGlobalScopes() - 软删除模型用
withTrashed()预加载时,whereHas()默认不包含软删记录,需显式调用whereHasMorph()或改用withTrashed()+whereRaw - 多态关联(如
Commentable)用whereHasMorph()替代whereHas(),否则会报错或查不到 - 测试时别只看
get()结果,用toSql()打印真实 SQL,尤其注意括号层级和 AND/OR 优先级


















