hyperf 的 paginate() 返回 hyperf\paginator\lengthawarepaginator 实例,含 current_page、last_page、per_page 等 laravel 风格字段,需 toarray() 转换并重映射为 page、pagesize、total 等前端兼容字段,且 appends() 必须在 toarray() 前调用。

Hyperf 的 paginate() 返回什么结构?
它返回 Hyperf\Paginator\LengthAwarePaginator 实例,不是数组,也不是 JSON 字符串。直接 return $query->paginate(10) 会触发对象的 jsonSerialize(),输出字段名如 current_page、last_page、per_page —— 这些是 Laravel 风格命名,但和主流前端分页组件(如 Element Plus、Ant Design)默认期望的 page、pageSize、totalPage 不一致。
常见错误现象:
- 前端调用
res.data.data拿不到列表,实际是res.data就是整个分页对象 - 点击页码后搜索参数丢失,因为
first_page_url等链接没保留 query string -
total是总数,last_page是总页数,别把last_page当成“最后一页的数据条数”
如何统一 API 响应格式?别直接 return paginate() 结果
Hyperf 默认不提供全局分页响应封装,必须手动组装。推荐在 Controller 层做一次转换,而不是依赖中间件或全局响应器 —— 因为分页元信息(如 total、page)需要和业务数据强绑定,中间件无法可靠提取。
实操建议:
- 用
$paginator->toArray()提取原始结构,再重键名:'page' => $arr['current_page']、'pageSize' => $arr['per_page']、'total' => $arr['total'] - 补上
hasNext和hasPrev布尔值($arr['next_page_url'] !== null),比前端自己算更可靠 - 如果用了
appends(),必须在toArray()前调用,否则 URL 参数不会进first_page_url等字段
paginate() 的 page 参数怎么传才生效?
Hyperf 的 paginate() 默认只读 $_GET['page'],不识别 ?p=2 或 ?offset=20。这不是 bug,是设计选择 —— 它要求你显式控制,避免隐式行为导致调试困难。
两种安全做法:
- 显式传参:
$query->paginate(15, false, ['page' => $this->request->input('p', 1)]),第二个参数false表示不自动解析 GET - 改配置:在
config/autoload/pagination.php中设'var_page' => 'p',这样它就会读$_GET['p'] - 注意:如果同时用了
appends(),且 append 的 key 和var_page冲突(比如都叫p),会导致 URL 参数重复
关联查询 + 分页时 total 为 0 怎么办?
常见于 User::with('profile')->paginate(10),结果 total 是 0,data 却有数据 —— 这是因为 Hyperf 在关联预加载下生成 COUNT SQL 时,可能因字段别名、表连接方式或子查询嵌套失败,fallback 到简单分页(LengthAwarePaginator 退化为 SimplePaginator)。
排查优先级:
- 先去掉
with(),确认主表分页是否正常;如果正常,问题出在关联逻辑 - 检查模型中是否定义了
$casts或$casts引发的类型转换干扰(如把idcast 成 string,COUNT 时出错) - 避免在
paginate()前调用selectRaw()或groupBy(),它们会让 COUNT 查询无法复用主查询结构
最稳的解法:用两次查询 —— 先 count() 得总数,再 forPage($page, $size)->get() 取数据,手动 new LengthAwarePaginator。虽然多一次查询,但可控性强,尤其适合复杂关联场景。











