ThinkPHP分页核心是paginate()接管查询链,必须链式调用(如User::where()->paginate()),不可在select()/all()结果上调用;render()依赖完整分页上下文,调用前不可toArray()或json();总数应使用total()而非count();需显式传false禁用自动参数解析以支持自定义size等参数。

ThinkPHP 的分页不是“渲染一个 HTML 字符串”那么简单,Paginator 实例的 render() 方法背后是一整套驱动+模板+上下文协同机制——漏掉其中一环,就会出现链接 404、参数丢失、样式错乱或直接白屏。
render() 调用时到底发生了什么
它不直接拼 HTML,而是委托给当前配置的驱动类(如 think\paginator\driver\Bootstrap)执行 render() 方法,该方法返回纯字符串。这个过程依赖三个关键输入:
- 分页对象自身状态:
$paginator->currentPage()、$paginator->lastPage()、$paginator->hasPages()等 - URL 上下文:由
withQuery()或appends()注入的参数,决定分页链接里带哪些 query 字段 - 渲染配置:传给
render()的数组(如['var_page' => 'p'])会覆盖默认值,但不会改变驱动类本身
常见错误是以为改了 theme 就能绕过驱动限制——其实 theme 只控制占位符展开逻辑,底层仍走 Bootstrap 驱动的 getLinks()、getPreviousButton() 等方法。想彻底换结构,必须换驱动类。
为什么复制 bootstrap4.php 到项目里还不管用
因为 ThinkPHP 6 不读取你随便放的 PHP 文件,它只认 config/paginate.php 里声明的驱动映射。即使你把文件放到 template/paginate/custom.php,没在配置中注册,render('custom') 也会 fallback 到默认驱动。
立即学习“PHP免费学习笔记(深入)”;
- 必须在
config/paginate.php中写:'driver' => 'custom',且'drivers' => ['custom' => \app\common\Paginator\CustomDriver::class] - 自定义驱动类必须实现
think\contract\PaginatorRenderInterface,否则启动时报Class does not implement interface - 类文件路径、命名空间、
use声明三者必须完全一致;错一个字母就Class not found
别试图在控制器里用 new Paginator() 手动构造——Paginator 是由 paginate() 内部创建并锁定驱动的,外部无法中途替换。
appends() 和 withQuery() 的实际行为差异
withQuery() 是查询构造器方法,它把 GET 参数提前“冻结”进查询上下文,后续所有分页链接都会自动带上;appends() 是 Paginator 实例方法,只影响当前这一次 render() 输出的链接。
-
Db::name('user')->where('status', 1)->withQuery(input('param.'))->paginate(10):安全,自动透传全部非 page 参数 -
$list->appends(request()->except(['page'])):也行,但要注意except()返回的是关联数组,键名必须合法(不能含点号或空格) - 混合使用会叠加参数,比如
withQuery(['a'=>1])+appends(['b'=>2])→ 链接含?a=1&b=2&page=2
最容易被忽略的是:如果路由含变量(如 /user/:id),withQuery() 不会自动捕获 :id 值,必须显式 appends(['id' => $id]),否则翻页后 URL 变成 /user?page=2,触发 404。
simple() 不是“简化版 render”,而是另一条路径
simple() 返回的是 SimplePaginator 实例,它跳过 COUNT(*) 查询,也不生成完整页码列表,只提供 hasMore()、nextPageUrl()、currentPage() 等最小集接口。
- 调用
$list->simple()->render()仍走 Bootstrap 驱动,但内部逻辑已切换为“上一页/下一页”模式 - 它不支持
%LINKS%占位符,theme里只能用%UP_PAGE%、%DOWN_PAGE%、%TOTAL%(此时total()返回 null) - 如果你需要手写
<nav>标签,直接用$list->simple()+ 模板变量更可控,比硬塞render()参数靠谱
真正难处理的不是怎么输出 HTML,而是让每一次分页链接都携带正确的上下文——URL 规则、路由变量、搜索参数、甚至 URL 后缀(如 .html)都得对齐,缺一不可。



















