paginator 的 url() 方法通过替换 urltpl 模板中的 [page] 生成带 page 参数的链接,如 ?page=2;它不解析原始 url,子目录部署需手动设置完整路径模板,否则导致 404。

url() 方法怎么拼出带 page 参数的链接
Paginator 的 url() 方法不自己解析 URL,而是依赖构造时传入的 urlTpl 模板和当前页码。默认模板是 ?page=[PAGE],调用 url(2) 就把 [PAGE] 替换成 2,得到 ?page=2。
它不会自动读取原始请求的完整 URL 路径或域名,只负责替换占位符。所以如果你在子目录部署(如 /admin/user),必须手动设置 urlTpl 为 /admin/user?page=[PAGE],否则生成的链接会变成根路径下的 ?page=2,导致 404。
常见错误包括:
- 直接 new
Paginator时不传urlTpl,又没配全局url_common,结果所有分页链接都指向首页 - 用了
appends()却没意识到它最终也是靠url()+urlTpl拼接,若urlTpl不含基础路径,appends加上的参数就挂在空 URL 上 - 在 CLI 或 API 场景下调用
url(),但urlTpl里硬编码了http://,导致生成不可用链接
appends() 是怎么把搜索参数塞进分页链接的
appends() 不修改 urlTpl,而是在 url() 返回的基础链接后追加查询字符串。比如 url(3) 返回 ?page=3,再调 appends(['keyword' => 'abc']),最终变成 ?page=3&keyword=abc。
关键点在于:它不做 URL 编码判断,直接 http_build_query 拼接,所以传入中文或特殊字符必须提前处理;更危险的是,如果传入 null 或空字符串,会生成 &keyword= 这种无效参数,某些 Nginx 配置下可能触发 400 错误。
安全做法是:
- 用
request()->except(['page'])代替手写键名列表,避免漏字段或传空值 - 对敏感参数(如
token、sign)做白名单过滤,不盲目appends(request()->param()) - 若参数来自 POST/JSON 请求,需在控制器里先提取并合并,不能依赖 GET 自动获取
为什么 render() 生成的链接有时缺参数,有时多出 index.php
render() 内部循环调用 url() 生成每一页链接,但它本身不决定路径结构——路径完全由 urlTpl 控制。如果你看到链接里多出 index.php(如 /index.php?page=2),说明 urlTpl 是从 Request::url() 或 Url::build() 推导来的,而当前环境未开启 URL 重写,框架 fallback 到了入口文件模式。
真正影响参数是否“丢失”的,是 appends() 调用时机和范围:
- 在控制器中对分页对象调一次
appends(),后续所有render()输出的链接都会带上这些参数 - 但如果在视图里多次调用
$list->appends(...)->render(),每次都会叠加,导致?page=2&keyword=a&keyword=b这类重复参数 - 使用
simple()模式时,render()只生成上一页/下一页,不生成数字页码,此时urlTpl若没预留足够参数位,下一页链接可能丢掉非 page 字段
paginate() 和 Paginator 的关系:谁在控制 url 生成逻辑
paginate() 是查询构造器上的方法,执行后返回一个 Paginator 实例;而 url() 是这个实例的方法。换句话说:paginate() 负责查数据、算总数、初始化分页上下文,但链接怎么拼,全交给 Paginator 实例的 urlTpl 和 appends 数据。
这意味着你无法通过改 paginate(15) 的参数来定制链接格式——除非传数组参数显式指定 'query' 和 'path'(TP8+ 支持),否则 urlTpl 仍由框架内部根据当前请求推导,不可控。
容易被忽略的一点是:Paginator 实例一旦生成,urlTpl 就固定了。后续调用 appends() 不会改变它,只是缓存追加参数;而 render() 每次都重新调用 url(),所以只要 urlTpl 出错,所有链接都错,不是某一页的问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











