本文目录导读:

在PHP中手动实现分页计算,需要处理几个关键参数:当前页码、每页显示数量、总记录数,然后计算总页数和偏移量。
以下是完整的实现方案:
基础分页计算函数
<?php
/**
* 计算分页参数
* @param int $total 总记录数
* @param int $perPage 每页显示数量(默认10)
* @param int $currentPage 当前页码(默认1)
* @return array 分页相关的所有参数
*/
function paginate($total, $perPage = 10, $currentPage = 1) {
// 1. 计算总页数(向上取整)
$totalPages = ceil($total / $perPage);
// 2. 确保总页数至少为1
$totalPages = max(1, $totalPages);
// 3. 当前页码安全检查
// 不能小于1,不能大于总页数
$currentPage = max(1, min($currentPage, $totalPages));
// 4. 计算偏移量(LIMIT子句的第一个参数)
$offset = ($currentPage - 1) * $perPage;
// 5. 返回所有计算好的参数
return [
'total' => $total, // 总记录数
'perPage' => $perPage, // 每页数量
'currentPage' => $currentPage, // 当前页码
'totalPages' => $totalPages, // 总页数
'offset' => $offset, // 用于SQL的OFFSET
'hasPrevious' => $currentPage > 1, // 是否有上一页
'hasNext' => $currentPage < $totalPages, // 是否有下一页
'previousPage' => $currentPage - 1, // 上一页页码
'nextPage' => $currentPage + 1, // 下一页页码
'startRecord' => $offset + 1, // 当前页开始的记录数(用于显示)
'endRecord' => min($offset + $perPage, $total) // 当前页结束的记录数
];
}
在数据库查询中使用
<?php
// 示例:查询用户列表并分页
// 1. 从GET获取当前页码,默认第一页
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 15; // 每页15条记录
// 2. 获取总记录数
$countSql = "SELECT COUNT(*) as total FROM users";
$countResult = $pdo->query($countSql);
$total = $countResult->fetch(PDO::FETCH_ASSOC)['total'];
// 3. 计算分页参数
$pagination = paginate($total, $perPage, $page);
// 4. 查询当前页的数据
$sql = "SELECT * FROM users ORDER BY id DESC LIMIT {$pagination['offset']}, {$perPage}";
$stmt = $pdo->query($sql);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 5. 输出数据
foreach ($users as $user) {
echo "用户名: " . htmlspecialchars($user['username']) . "<br>";
}
// 6. 输出分页导航
echo renderPagination($pagination);
?>
生成分页导航HTML
<?php
/**
* 生成分页导航HTML
* @param array $pagination paginate()函数的返回值
* @return string 分页HTML
*/
function renderPagination($pagination) {
// 如果只有一页,不需要显示分页
if ($pagination['totalPages'] <= 1) {
return '';
}
$html = '<nav class="pagination">';
$html .= '<ul>';
// 上一页按钮
if ($pagination['hasPrevious']) {
$html .= '<li><a href="?page=' . $pagination['previousPage'] . '">« 上一页</a></li>';
} else {
$html .= '<li class="disabled"><span>« 上一页</span></li>';
}
// 页码数字(显示带限制的页码范围)
$start = max(1, $pagination['currentPage'] - 2); // 当前页前2页
$end = min($pagination['totalPages'], $pagination['currentPage'] + 2); // 当前页后2页
for ($i = $start; $i <= $end; $i++) {
if ($i == $pagination['currentPage']) {
$html .= '<li class="active"><span>' . $i . '</span></li>';
} else {
$html .= '<li><a href="?page=' . $i . '">' . $i . '</a></li>';
}
}
// 下一页按钮
if ($pagination['hasNext']) {
$html .= '<li><a href="?page=' . $pagination['nextPage'] . '">下一页 »</a></li>';
} else {
$html .= '<li class="disabled"><span>下一页 »</span></li>';
}
$html .= '</ul>';
// 显示记录信息
$html .= '<div class="page-info">';
$html .= '共 ' . $pagination['total'] . ' 条,';
$html .= '第 ' . $pagination['startRecord'] . '-' . $pagination['endRecord'] . ' 条,';
$html .= '当前第 ' . $pagination['currentPage'] . '/' . $pagination['totalPages'] . ' 页';
$html .= '</div>';
$html .= '</nav>';
return $html;
}
?>
完整示例(PDO使用)
<?php
// 完整的分页实现示例
// 数据库连接
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$pdo = new PDO($dsn, 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 分页参数
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
try {
// 查询总数
$stmt = $pdo->query("SELECT COUNT(*) FROM articles");
$total = $stmt->fetchColumn();
// 计算分页
$pagination = paginate($total, $perPage, $page);
// 查询数据
$sql = "SELECT * FROM articles ORDER BY created_at DESC
LIMIT {$pagination['offset']}, {$pagination['perPage']}";
$stmt = $pdo->query($sql);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 显示数据
foreach ($articles as $article) {
echo "<h2>{$article['title']}</h2>";
echo "<p>" . mb_substr($article['content'], 0, 100) . "...</p>";
echo "<hr>";
}
// 显示分页
echo renderPagination($pagination);
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
带搜索条件的分页
<?php
/**
* 带搜索条件的分页示例
*/
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
// 构建条件
$where = '';
$params = [];
if ($search !== '') {
$where = "WHERE title LIKE ? OR content LIKE ?";
$params = ["%$search%", "%$search%"];
}
// 查询总数
$stmt = $pdo->prepare("SELECT COUNT(*) FROM articles $where");
$stmt->execute($params);
$total = $stmt->fetchColumn();
// 计算分页
$pagination = paginate($total, $perPage, $page);
// 查询数据
$sql = "SELECT * FROM articles $where
ORDER BY created_at DESC
LIMIT {$pagination['offset']}, {$pagination['perPage']}";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 分页链接需要保留搜索参数
$queryString = http_build_query(['search' => $search]);
// HTML中的分页链接改为: "?page={$i}&{$queryString}"
- 总页数 =
ceil($total / $perPage),向上取整 - 偏移量 =
($currentPage - 1) * $perPage - 当前页码限制:不能小于1,不能大于总页数
- SQL使用:
LIMIT offset, perPage - 页码范围控制:当页数很多时,只显示部分页码(如前后各2个),避免输出太多按钮
这个实现方式不依赖任何框架,可以用于纯PHP项目,也可以很容易地适配到ThinkPHP、Laravel等框架中。