如何在 Laravel 中基于查询参数正确实现每页 20 条的数据分页

如何在 Laravel 中基于查询参数正确实现每页 20 条的数据分页

本文详解如何修复基于 `offset` 查询参数的手动分页逻辑错误,指出原始代码中页码与偏移量混淆的问题,并提供使用 laravel 集合 `chunk()` 方法及更健壮的 `forpage()` 方案。

你当前的分页逻辑存在一个关键概念性错误:将查询参数 offSet(应为 偏移量,即跳过的记录数)误当作 页码(page number)来使用。

在你的代码中:

$page = $request->get('offSet'); // ❌ 错误:offSet 是偏移量,不是页码
$take = 20;
$skip = ($page - 1) * $take; // 若 offSet=2,则 skip = 20 → 跳过前20条,取第21–40条 → 实际是第2页,但语义混乱!

这导致行为不可预测:当用户传 offSet=1 时,skip = 0,显示第 1–20 条(看似“第1页”);但传 offSet=2 时,skip = 20,显示第 21–40 条——这其实是按偏移量直接切片,而非标准分页。而 offSet=4 会跳过 60 条,若总数据不足 80 条,结果为空,因此“不工作”。

✅ 正确做法有两种推荐方案:

方案一:使用 forPage($page, $perPage)(推荐 ✅)

这是 Laravel 集合原生支持的标准分页方法,语义清晰、零越界风险:

Viggle AI

Viggle AI

Viggle AI是一个AI驱动的3D动画生成平台,可以帮助用户创建可控角色的3D动画视频。

下载

$results = collect($response->json()['results']);
$page = (int) $request->input('page', 1); // 默认第1页
$perPage = 20;

$paginated = $results->forPage($page, $perPage)->values()->all();
// ->values() 重置键名,确保返回连续数字索引数组

✅ 优势:自动处理页码越界(如请求第100页但只有50条数据,返回空集合)、无需手动计算 skip,符合 REST 分页惯例(?page=1, ?page=2)。

方案二:若坚持用 offset 参数(如兼容旧 API),请直译为偏移量

$offset = max(0, (int) $request->input('offSet', 0));
$limit = 20;
$paginated = $results->slice($offset, $limit)->values()->all();

⚠️ 注意:此时 offSet=0 → 第1页,offSet=20 → 第2页……需前端严格按 offset = (page-1)*20 构造 URL,否则语义断裂。

❌ 不推荐:chunk() + 数组访问

答案中提到的 $results->chunk(20)[$request->input(‘offset’)] 存在严重隐患:

// 示例:$results 有 45 条 → chunk(20) 得到集合 [ [0..19], [20..39], [40..44] ]
// 若 $request->input('offset') == 3 → 尝试访问索引 3 → PHP Notice: Undefined offset

chunk() 返回的是「按块分组的集合」,其键是块序号(0-indexed),但 offset 参数通常代表偏移量(非块号),二者单位不同,强行映射极易越界崩溃。

最佳实践总结

  • ✅ 前端传参统一用 ?page=1&per_page=20,后端用 forPage();
  • ✅ 必须校验输入:(int) $request->input(‘page’, 1) 防止非数字注入;
  • ✅ 总数未知时,可用 count() + forPage() 安全分页;若需分页元信息(总页数/总数),建议改用数据库分页(paginate());
  • ❌ 避免手动 skip/take 计算,除非你完全掌控数据规模且需极致性能。

修正后的完整示例:

$results = collect($response->json()['results']);
$page = max(1, (int) $request->input('page', 1));
$perPage = 20;

$items = $results->forPage($page, $perPage)->values()->all();
return response()->json([
    'data' => $items,
    'meta' => [
        'current_page' => $page,
        'per_page' => $perPage,
        'total' => $results->count(),
        'last_page' => (int) ceil($results->count() / $perPage),
    ]
]);

https://www.php.cn/faq/1973194.html

发表回复

Your email address will not be published. Required fields are marked *