ThinkPHP 6 的 paginate() 默认返回 think\Paginator\Bootstrap5 实例,是含数据与分页元信息的封装对象,非响应对象,不可直接 return;API 中需调用 toArray() 或 items() 等方法提取结构化数组再构造 JSON 响应。

ThinkPHP 6 的 paginate() 默认返回什么?
直接调用 paginate()(比如 $user->paginate(10))返回的是一个 think\Paginator\Bootstrap5(或对应驱动)实例,不是响应对象。它本质是数据 + 分页元信息的封装,不能直接 return 给前端——否则会抛出 Cannot use object of type think\Paginator\Bootstrap5 as array 或渲染出模板而非 JSON。
怎么让分页结果变成可 return 的响应?
核心就一条:用 toArray() 提取结构化数据,再手动构造响应;或者用 render() 单独处理分页 HTML(仅限模板场景)。API 接口必须走 toArray() 路线:
-
toArray()返回关联数组,含data、current_page、last_page、per_page、total等字段,可直接return json($paginator->toArray()) - 别用
json($paginator)—— 它会尝试序列化整个 Paginator 对象,触发不可序列化属性报错 - 如果用了
with(['xxx'])预加载,toArray()会自动处理关联数据,无需额外转换
为什么 return $paginator 在 API 下不工作?
ThinkPHP 的自动响应机制只对 think\Response 实例、数组、字符串、JSON 对象等基础类型生效。Paginator 是个“视图层”对象,设计初衷是配合模板(如 {$list->render()})使用。在 JSON API 场景下硬 return 它,框架无法识别其响应意图,且内部含 Closure、Resource 等不可序列化成分。
- 常见错误现象:
Fatal error: Uncaught Exception: Serialization of 'Closure' is not allowed - 兼容性注意:TP6.0+ 的 Paginator 不再实现
ArrayAccess和JsonSerializable,所以不能当数组或 JSON 对象直用 - 性能无额外开销:
toArray()是浅拷贝,不重复查库,只是格式重组
完整响应示例(TP6.3+)
这是最简可用写法,兼顾字段清晰和前端友好:
立即学习“PHP免费学习笔记(深入)”;
public function index()
{
$list = User::where('status', 1)
->order('id', 'desc')
->paginate(15);
return json([
'code' => 0,
'msg' => 'success',
'data' => $list->items(), // 只要数据列表
'meta' => [
'current_page' => $list->currentPage(),
'last_page' => $list->lastPage(),
'per_page' => $list->listRows(),
'total' => $list->total(),
],
]);
}
注意 $list->items() 比 $list->toArray()['data'] 更干净——它跳过分页元信息,纯数据数组,避免嵌套多一层。
真正容易被忽略的是:Paginator 的 currentPage() 依赖 URL 中的 page 参数,默认从 $_GET 读;如果你用 POST 或自定义参数名(如 offset),得提前调用 Page::setPageName('offset'),否则页码永远是 1。



















