最可控方式是直接 new LengthAwarePaginator,需传入当前页数据数组、总条数、当前页码、每页数量;它不自动切片、不校验页码、无隐式行为,URL 需手动处理 query 参数。

怎么用 LengthAwarePaginator 手动构造分页结果
直接 new 一个 LengthAwarePaginator 是最可控的方式,适用于原始数组、非 Eloquent 查询(比如 DB::select)、或需要完全自定义分页逻辑的场景。
常见错误是传错参数顺序或类型:第 1 个参数必须是当前页数据(array),第 2 个是总条数(int),不是总页数;第 3 个是当前页码(int),第 4 个是每页数量(int)。
-
LengthAwarePaginator不会自动切片,你得自己用array_slice()提前截好当前页的数据 - 当前页码默认从 1 开始,传 0 或负数会导致链接异常(如生成
?page=0) - 别把
Collection直接当array传——先调$collection->toArray()
示例:
use Illuminate\Pagination\LengthAwarePaginator;
<p>$rawData = DB::select('SELECT <em> FROM users WHERE status = ?', [1]);
$total = DB::table('users')->where('status', 1)->count(); // 必须单独查总数
$perPage = 15;
$page = request()->input('page', 1);
$offset = ($page - 1) </em> $perPage;
$items = array_slice($rawData, $offset, $perPage);</p><p>$paginator = new LengthAwarePaginator($items, $total, $perPage, $page);
LengthAwarePaginator 的 URL 生成为什么总是跳到首页
默认情况下,LengthAwarePaginator 用的是 Laravel 当前请求的完整 URL,但如果你在非标准路由(比如 API 路由、子域名、带 query 参数的页面)里手动构造它,appends() 不会自动继承原有参数,翻页链接就丢了上下文。
- 必须显式调用
$paginator->withQueryString()(Laravel 9+)或$paginator->appends(request()->query())(旧版) - 如果用了
route()生成链接(比如后台管理页),得手动传 route 参数:$paginator->onEachSide(1)->withPath(route('admin.users.index'))->appends(['search' => $search]) - 注意:调用
appends()后不能再链式调用withQueryString(),后者会覆盖前者
和 paginate() 比,LengthAwarePaginator 少了哪些隐式行为
paginate() 是 Eloquent 的语法糖,它内部做了几件事:count() 总数查询、forPage() 切片、自动绑定 request()->page、设置默认 perPage、注入分页器实例。而 LengthAwarePaginator 是纯构造器,这些全得你来。
- 没有自动检测
per_page请求参数,你得自己读request()->input('per_page', 15) - 不校验页码合法性(比如
page > lastPage),越界时只返回空数组,不会跳转或报错 - 不兼容
CursorPaginator的游标逻辑,不能混用 - 渲染时缺失
onEachSide()等链式方法的默认值,要写全:$paginator->onEachSide(2)->links()
性能敏感场景下,LengthAwarePaginator 的 count 查询怎么优化
手动构造时,$total 往往来自一次额外的 COUNT(*) 查询,这是最大性能瓶颈。尤其在大表 + 复杂 where 条件下,count 可能比主查询还慢。
- 能用缓存就缓存总数,比如
Cache::remember("user_count_{$key}", 3600, fn() => DB::table(...)->count()) - 避免在 count 查询里用
SELECT *或 JOIN ——count()只需表名和 where,字段和关联无关 - 如果业务允许近似值(比如后台用户量统计),可用
SHOW TABLE STATUS查rows字段(MySQL),但注意它是估算值 - 对超大数据集,考虑改用
SimplePaginator(无总页数),或前端无限滚动 + 游标分页
真正麻烦的不是怎么写这个类,而是每次都要重新想清楚:总数哪来、当前页数据怎么截、URL 怎么续、边界怎么兜底——少一步,分页就断在某个奇怪的地方。


















