
withCount 不能用于一对一关联,只对一对多、多对多有效;用错关联类型会静默返回 0,而不是报错。
withCount 基本用法和属性名生成规则
调用 withCount('comments') 后,模型实例会自动添加一个叫 comments_count 的动态属性,值就是关联记录数。这个命名是固定的:关联方法名 + _count。
- 如果关联方法叫
likes,统计结果存到likes_count - 不支持直接传字段,比如
withCount('comments.id')是无效语法 - 多个统计可一次写全:
withCount(['comments', 'likes']),对应生成comments_count和likes_count
自定义统计属性名(TP 5.0.7+)
想避开默认的 _count 后缀?用数组键值对形式指定别名,例如 withCount(['comments' => 'comm_count'])。
- 生成的属性名就是你写的字符串,如
comm_count,不是comments_comm_count - 别名只影响最终读取的属性名,SQL 子查询别名仍由框架内部生成,不可控
- 闭包条件 + 自定义别名同时使用时,别名可能被忽略(实测部分版本存在 bug),建议优先用闭包过滤,别名仅作辅助
带条件的关联统计(闭包方式)
要统计「已审核的评论数」或「近 7 天的点赞数」,必须用闭包传条件,否则 where 写在主查询里会错位。
立即学习“PHP免费学习笔记(深入)”;
- 正确写法:
withCount(['comments' => function($query) { $query->where('status', 1); }]) - 错误写法:在
withCount()外再链式调where(),这会影响主表筛选,不是关联表过滤 - 闭包内可链多个条件,但不能调用
select()或order()等影响结果集结构的方法 - 注意:闭包里写的表字段必须属于关联表,主表字段会报 SQL 错误
和 with() 预载入混用的注意事项
可以同时用 with('comments') 和 withCount('comments'),但要注意性能和数据一致性。
- 两者会各自发起子查询,不是复用同一个 JOIN —— 即使关联逻辑一致,SQL 也分开执行
- 如果关联数据量大,
with()拉全量 +withCount()再查一遍 COUNT,不如拆成两个独立请求更可控 - 分页场景下,
paginate()会先执行 COUNT 查询,此时withCount的子查询也会被执行两次(一次 count,一次 select),容易拖慢响应
最易被忽略的一点:withCount 的统计结果是整型,但不会自动转为 int 类型——它作为模型动态属性存在,var_dump 时可能是 string 或 int,取决于数据库驱动返回值类型;做数值比较(如 $user->comments_count > 0)前,最好显式 (int) 转换。



















