ThinkPHP的paginate()默认不继承除page外的GET参数,必须通过query参数显式传入搜索、筛选等业务参数;query中混入page会导致翻页失效,appends()会覆盖同名query参数且仅对render()有效。

query参数不是自动继承,而是必须显式传入
ThinkPHP 的 paginate() 默认只识别 page(或你配置的 var_page)参数,其他 GET 参数如 keyword、status、category 一律不带入分页链接——这不是遗漏,是设计使然。它不会偷偷把 request()->param() 全部塞进去,除非你明确告诉它要带哪些。
常见错误现象:搜索后翻页,URL 从 ?keyword=php&page=1 变成 ?page=2,条件全丢。原因就是没在 paginate() 第三个参数里配 query。
-
query是个纯数组,键为 URL 参数名,值为对应值,例如['keyword' => input('keyword'), 'status' => input('status')] - 用
request()->param()最省事,但必须过滤掉分页相关键,否则page和var_page会被重复携带,导致跳转错乱 - 推荐写法:
array_diff_key(request()->param(), ['page' => '', 'p' => '', 'page_no' => '']),适配多种页码参数名 - 如果用了路由变量(如
route('user.index', ['id' => 123])),它们不会自动进入分页链接,得手动加进query数组
query和appends()的优先级与叠加规则
appends() 是链式补参方法,但它不改变原始分页对象的 query 基础,只是在 render() 时临时拼接。它的行为有明确优先级:
- 若控制器中已通过
paginate([... 'query' => $q])传入query,再调用$list->appends(['a' => 1]),最终链接会包含query里的所有参数 +a=1 - 如果
appends()和query有同名键(比如都设了status),appends()的值会覆盖query里的值 -
appends()对simple()模式有效,但对simplePaginate()返回的对象无效(因为后者不走完整分页逻辑) - 不要在模板循环里反复调用
appends(),每次调用都返回新对象,浪费内存且易出错
query里混入page会导致翻页失效
当你自定义了 var_page => 'p',却还在 query 数组里硬写 'page' => input('page'),就会出问题:框架用 p=2 解析当前页,但 query 又强行塞了个空的 page=,结果分页链接变成 ?p=2&page=&keyword=xxx,某些 Nginx 或代理会截断或忽略空值,造成跳转到第 1 页。
立即学习“PHP免费学习笔记(深入)”;
- 正确做法:统一用
input($config['var_page'] ?? 'page')取当前页码,别在query里再写页码键 -
query只负责“非分页控制类”的业务参数,比如搜索、筛选、排序字段 - 如果需兼容新旧参数名(如同时支持
p=2和page=2),取值逻辑写成input('p/d', input('page/d', 1)),但query里仍只透传业务参数
query参数污染:多次paginate调用引发的链接冗余
在同一个控制器方法里,因条件分支多次调用 paginate()(比如不同 status 走不同查询),若复用同一个 $config 数组变量,query 容易被反复 merge,导致分页链接出现重复参数,如 ?status=1&status=2&keyword=abc。
- 每次调用前应新建
query数组,避免引用共享变量 - 推荐用
request()->only(['keyword', 'category', 'sort'])替代request()->param(),精准控制透传字段 - 别用
input('get.')构造query,它可能混入 POST 数据,污染 GET 链接 - 如果参数含数组(如复选框
type[]=1&type[]=2),request()->param()能自动解析为 PHP 数组,但手动构造时需确保键名一致,否则http_build_query()会出错
query 的合并逻辑其实非常朴素:它不智能、不推断、不猜测——你给什么,它就拼什么。最容易被忽略的是,你以为它“应该”带上的参数,恰恰是它默认忽略的;而你随手塞进去的 page,反而成了翻页跳变的根源。



















