ThinkPHP 6 的 paginate() 方法直接调用即可实现分页,自动执行 COUNT 和数据查询;需避免链式调用 select() 等终止方法,参数支持整数或含 query 的数组以保留 URL 参数,API 返回应提取 items() 和分页元信息而非 toArray()。

ThinkPHP 6 的 paginate() 方法怎么用
直接调用模型或查询构造器的 paginate() 就能分页,不用手写 SQL LIMIT 和 COUNT。它会自动执行两次查询:一次查总数(用于计算总页数),一次查当前页数据。
常见错误是把 paginate() 放在 select() 或 get() 后面——这是错的,paginate() 是终止方法,不能链式跟其他查询方法。
-
UserModel::where('status', 1)->paginate(10)✅ 正确 -
UserModel::where('status', 1)->select()->paginate(10)❌ 报错或返回空 - 参数传整数表示每页条数;传数组可定制,比如
['query' => ['tab' => 'user']]保留 URL 参数 - 默认从 $_GET['page'] 读取当前页码,想改字段名用
->paginate(['page' => 'p'])
如何渲染分页 HTML 并保持自定义参数
模板里直接输出 $list->render() 就能生成带样式的分页链接,但默认只保留 page 参数,其他 GET 参数(如搜索关键词、分类 ID)会丢失。
必须显式传递 query 参数才能保留上下文。比如搜索页带 ?keyword=php&category=2,不处理的话翻页后 keyword 就没了。
立即学习“PHP免费学习笔记(深入)”;
- 控制器中:
$list = UserModel::where('name', 'like', "%{$keyword}%")->paginate(['list_rows' => 15, 'query' => ['keyword' => $keyword, 'category' => $category]]) - 模板中:
{$list->render()|raw}(ThinkPHP 6 模板需加|raw防转义) - 如果参数多且动态,建议用
input()全量透传:'query' => request()->param(),但注意过滤敏感键(如 password)
为什么分页总数不准?COUNT 查询被干扰了
当查询里含 group by、distinct、union 或复杂 JOIN 时,paginate() 自动生成的 COUNT 查询可能出错,导致总页数异常(比如显示 100 页但实际只有几条数据)。
根本原因是 ThinkPHP 默认用 COUNT(*) 包裹原始查询,而某些语法不支持嵌套 COUNT。
- 简单方案:手动指定总数,
paginate(15, false, ['total' => $customTotal]),$customTotal自己用count()算 - JOIN 场景下,优先用
withCount()替代子查询,避免 COUNT 失效 - 实在绕不开复杂 SQL,就别用
paginate(),改用limit()+ 手动算页码和总数
分页数据在 API 接口里怎么返回结构更合理
Web 页面用 render() 没问题,但 API 返回 JSON 时,直接吐 $list 对象会把分页对象的大量内部属性(如 options、currentPage)全暴露出去,既冗余又不标准。
应该提取关键字段组装成规范结构,前端才好解析。
- 推荐格式:
['data' => $list->items(), 'meta' => ['total' => $list->total(), 'per_page' => $list->listRows(), 'current_page' => $list->currentPage(), 'last_page' => $list->lastPage()]] - 不要用
$list->toArray(),它包含大量框架私有字段 - 如果用了资源类(Resource),在
toArray()里单独处理分页字段,避免污染数据主体
分页看着简单,但 query 参数透传、COUNT 兼容性、API 结构这三处最容易漏掉或写错,尤其上线后用户反馈“点下一页搜不到结果”,八成是 query 没传全。



















