ThinkPHP 8.1 分页 total 不准的根源是 COUNT 与实际查询语义不一致:一、关联查询时 COUNT 主表而非关联结果;二、软删除未前置导致统计遗漏;三、分页后数据加工丢失原始 Page 对象;四、高并发下 COUNT 结果过期。

ThinkPHP 8.1 分页中 total 统计不准,核心问题不是框架 bug,而是 COUNT 逻辑与实际查询语义不一致 —— 它常数错对象、数错条件、或被中间操作干扰。
一、关联查询时 total 是主表行数,不是关联结果行数
比如用 UserModel::with('orders')->paginate(10),total() 返回的是用户总数,但页面展示的是「带订单的用户」;若部分用户无订单,with() 补 null,实际有数据的分页项变少,而 total 仍按全量用户算,导致页码跳转后空白或漏数据。
根本原因:paginate() 内部先执行 SELECT COUNT(*) FROM user,再执行 SELECT * FROM user LEFT JOIN order … LIMIT,二者 WHERE 和 JOIN 不同步。
修复方式:
立即学习“PHP免费学习笔记(深入)”;
- 弃用
with(),改用显式 JOIN:Db::table('user')->alias('u')->join('order o', 'u.id = o.user_id', 'INNER'),再调paginate() - 如需保留模型结构,用
whereHas('orders')替代with(),确保 COUNT 和 SELECT 共享相同筛选逻辑 - 1:N 展开分页(如每个用户显示多条订单)必须放弃 paginate(),改用子查询 + 手动分页
二、软删除未生效导致 COUNT 包含已删除记录
withTrashed() 必须放在链式调用最前端,否则会被后续 where 或 paginate() 忽略。例如:UserModel::where('status', 1)->withTrashed()->paginate(10) 中,withTrashed() 实际无效,COUNT 仍走默认软删过滤逻辑,造成总数偏少。
修复方式:
立即学习“PHP免费学习笔记(深入)”;
- 正确写法:
UserModel::withTrashed()->where('status', 1)->paginate(10) - 关联模型也要单独处理:
with(['orders' => function($q) { $q->withTrashed(); }]) - 若需精确 total,禁用自动 COUNT,手动传入:
paginate(10, false, ['total' => $count]),其中$count用带withTrashed()的独立 COUNT 查询获取
三、分页前有数据处理或流式转换,丢失原始 Page 对象
常见于后台导出或字段加工场景:查出 $page = UserModel::paginate(15) 后,对 $page->items() 做 array_map 或封装成新数组,再返回给前端 —— 此时 $page->total() 仍存在,但若你把 $page->items() 赋值给新变量并重新 new PageInfo 或自定义响应结构,原始 total 信息极易丢失或被覆盖。
修复方式:
立即学习“PHP免费学习笔记(深入)”;
- 避免对分页结果做破坏性转换;如必须加工,保留原始
$page实例,仅对其items()进行映射,最后用$page->toArray()输出 - 若使用自定义响应包装,显式提取并透传:
'total' => $page->total(), 'data' => $processedItems - 检查是否误将
$page->items()当作普通数组重新分页,导致二次分页逻辑覆盖原 total
四、深分页 + 高频变更下 COUNT 结果过期或不准
用户刚删了一批数据,但分页仍按旧总数渲染页码;或并发插入时,第一页查完,第二页 COUNT 已变化 —— 这不是 TP 问题,而是业务状态与统计时机不同步。
修复方式:
立即学习“PHP免费学习笔记(深入)”;
- 删除/新增后,强制重新 COUNT:
UserModel::where($condition)->count(),不要复用缓存或上一次结果 - 高并发场景下,禁用 total:用
paginate(20, false),靠查 21 条判断是否有下一页,避开 COUNT 竞态 - 数据有序且无需跳页时,直接切到游标分页:
WHERE id > :last_id ORDER BY id LIMIT 20,彻底规避 offset/count 不一致问题



















