PHP接口数据分页的简单实现方法

分页接口需要解决的核心问题

列表接口直接返回全表数据,在数据量增长后会带来响应体过大、查询变慢、前端渲染卡顿等问题。分页的本质是把一次全量读取拆成多次定量读取,接口需要明确三件事:取第几页、每页多少条、总共有多少条。围绕这三点,PHP 侧的实现可以拆成参数接收、参数校验、偏移量计算、数据库查询、结果组装五个步骤。

参数接收与校验

客户端通常通过 page 和 pageSize 两个查询参数控制分页。接收后必须做类型转换与边界限制,否则会出现负数偏移、超大分页拖垮数据库等问题。

$page     = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$pageSize = isset($_GET['pageSize']) ? (int)$_GET['pageSize'] : 10;

// 边界处理
$page     = max(1, $page);
$pageSize = min(max(1, $pageSize), 100); // 单页上限建议 50~100

校验要点:

  • page 最小为 1,小于 1 时归一到 1,避免出现负偏移量
  • pageSize 设置上限,防止客户端传入 pageSize=100000 造成慢查询
  • 强制转为整型,过滤 1abc、1.5 这类非法输入
  • 若接口有固定业务场景,可对 pageSize 提供白名单(如 10/20/50)

偏移量计算与 SQL 查询

偏移量公式为 offset = (page - 1) * pageSize。注意 LIMIT 的两个参数顺序是「偏移量, 条数」,写反是常见错误。

$offset = ($page - 1) * $pageSize;

// 使用 PDO 预处理,LIMIT 参数需绑定为整数
$stmt = $pdo->prepare(
    'SELECT id, title, created_at FROM articles ORDER BY id DESC LIMIT :offset, :limit'
);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->bindValue(':limit', $pageSize, PDO::PARAM_INT);
$stmt->execute();
$list = $stmt->fetchAll(PDO::FETCH_ASSOC);

两个实践细节:

  • LIMIT 占位符必须用 PDO::PARAM_INT 绑定,字符串绑定在部分驱动下会报语法错误
  • 排序字段要有索引,ORDER BY id DESC 配合主键可避免额外排序开销

总条数查询与响应结构

前端做页码导航需要总条数,因此要额外执行一次 COUNT(*)。两次查询条件应保持一致,否则会出现「总数与列表对不上」的问题。

$countStmt = $pdo->prepare('SELECT COUNT(*) FROM articles');
$countStmt->execute();
$total = (int)$countStmt->fetchColumn();

$totalPages = (int)ceil($total / $pageSize);

echo json_encode([
    'code' => 0,
    'data' => [
        'list'       => $list,
        'page'       => $page,
        'pageSize'   => $pageSize,
        'total'      => $total,
        'totalPages' => $totalPages,
    ],
], JSON_UNESCAPED_UNICODE);

响应字段建议固定为 list / page / pageSize / total / totalPages,前端只需按约定取值,不必每次适配新结构。

边界与性能注意事项

场景 处理方式
页码超出总页数 list 返回空数组,total 照常返回,不报错
总数为 0 totalPages 返回 0,前端隐藏分页组件
深分页(如第 10000 页) 大偏移量扫描成本高,可改用游标分页(WHERE id < lastId)
数据频繁增删 翻页过程中总数可能变化,属正常现象,无需强一致
高并发列表 对 COUNT(*) 结果做短时缓存,降低数据库压力

可执行结论

实现一个可用的分页接口,最小闭环是:接收并强制转换 page、pageSize → 限制取值范围 → 计算 offset → 用预处理语句执行带 LIMIT 的查询 → 单独查询总数 → 按统一结构返回。数据量不大时这套方案足够;当单表数据达到百万级且存在深分页需求时,再评估游标分页或基于索引的优化方案。

© 版权声明

相关文章