Appearance
17.3 分页功能
为什么需要分页
在 Web 应用程序中,当数据量较大时,分页是一种常见的技术,它可以:
- 提高页面加载速度:只加载当前页的数据,减少数据传输量
- 改善用户体验:避免一次性显示过多数据,使页面更整洁
- 减轻服务器负担:减少数据库查询和数据处理的压力
- 便于导航:用户可以方便地浏览不同页的数据
PHP 实现分页的方法
1. 基本分页原理
分页的基本原理是:
- 确定每页显示的记录数:通常为 10、20 或 50 条
- 计算总记录数:从数据库中查询总记录数
- 计算总页数:总记录数除以每页显示的记录数,向上取整
- 计算当前页的起始位置:(当前页码 - 1) * 每页显示的记录数
- 查询当前页的数据:使用 LIMIT 子句查询当前页的数据
- 生成分页导航:根据当前页码和总页数生成分页链接
2. 实现步骤
步骤 1:设置分页参数
php
// 设置分页参数
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1; // 当前页码
$per_page = 10; // 每页显示的记录数
$start = ($page - 1) * $per_page; // 起始位置步骤 2:查询总记录数
php
// 查询总记录数
$sql = "SELECT COUNT(*) as total FROM users";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
$total = $row['total'];
// 计算总页数
$total_pages = ceil($total / $per_page);步骤 3:查询当前页的数据
php
// 查询当前页的数据
$sql = "SELECT * FROM users ORDER BY id DESC LIMIT $start, $per_page";
$result = mysqli_query($conn, $sql);
$users = mysqli_fetch_all($result, MYSQLI_ASSOC);步骤 4:生成分页导航
php
// 生成分页导航
$pagination = '';
if ($total_pages > 1) {
// 上一页
if ($page > 1) {
$pagination .= '<a href="?page=' . ($page - 1) . '">上一页</a>';
}
// 页码链接
for ($i = 1; $i <= $total_pages; $i++) {
if ($i == $page) {
$pagination .= '<span class="current">' . $i . '</span>';
} else {
$pagination .= '<a href="?page=' . $i . '">' . $i . '</a>';
}
}
// 下一页
if ($page < $total_pages) {
$pagination .= '<a href="?page=' . ($page + 1) . '">下一页</a>';
}
}3. 使用第三方库实现分页
使用 Pagerfanta
Pagerfanta 是一个功能强大的分页库,支持多种 ORM 和模板引擎:
安装
bash
composer require pagerfanta/pagerfanta使用
php
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Pagerfanta;
// 数据
$users = [/* 数据数组 */];
// 创建适配器
$adapter = new ArrayAdapter($users);
// 创建分页器
$pagerfanta = new Pagerfanta($adapter);
$pagerfanta->setMaxPerPage(10);
$pagerfanta->setCurrentPage($page);
// 获取当前页的数据
$currentPageResults = $pagerfanta->getCurrentPageResults();
// 生成分页导航
$paginationHtml = $pagerfanta->getNavigation();使用 Laravel 的分页
如果使用 Laravel 框架,可以使用其内置的分页功能:
php
// 查询数据并分页
$users = DB::table('users')->paginate(10);
// 在视图中显示分页
{{ $users->links() }}分页最佳实践
- 使用索引:为查询的字段添加索引,提高查询速度
- 使用 LIMIT 子句:只查询当前页的数据,减少数据传输量
- 缓存总记录数:如果数据不频繁变化,可以缓存总记录数,减少数据库查询
- 优化分页导航:只显示当前页附近的页码,避免显示过多页码
- 处理边界情况:处理第一页和最后一页的情况,避免出现无效的分页链接
- 支持 SEO:使用 rel="prev" 和 rel="next" 标签,帮助搜索引擎索引分页内容
- 提供每页显示条数选项:允许用户选择每页显示的记录数
实战演练
场景:用户列表分页
php
<?php
// 连接数据库
$conn = mysqli_connect('localhost', 'root', '', 'test');
// 设置分页参数
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$per_page = 10;
$start = ($page - 1) * $per_page;
// 查询总记录数
$sql = "SELECT COUNT(*) as total FROM users";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
$total = $row['total'];
$total_pages = ceil($total / $per_page);
// 查询当前页的数据
$sql = "SELECT * FROM users ORDER BY id DESC LIMIT $start, $per_page";
$result = mysqli_query($conn, $sql);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户列表</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.pagination {
display: flex;
list-style: none;
padding: 0;
}
.pagination li {
margin: 0 5px;
}
.pagination a {
display: block;
padding: 5px 10px;
border: 1px solid #ddd;
text-decoration: none;
color: #007bff;
}
.pagination a:hover {
background-color: #f8f9fa;
}
.pagination .active a {
background-color: #007bff;
color: white;
border-color: #007bff;
}
</style>
</head>
<body>
<div class="container mt-5">
<h1>用户列表</h1>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>创建时间</th>
</tr>
</thead>
<tbody>
<?php while ($user = mysqli_fetch_assoc($result)): ?>
<tr>
<td><?php echo $user['id']; ?></td>
<td><?php echo $user['username']; ?></td>
<td><?php echo $user['email']; ?></td>
<td><?php echo $user['created_at']; ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
<!-- 分页导航 -->
<?php if ($total_pages > 1): ?>
<ul class="pagination justify-content-center">
<!-- 上一页 -->
<?php if ($page > 1): ?>
<li class="page-item">
<a class="page-link" href="?page=<?php echo $page - 1; ?>">上一页</a>
</li>
<?php else: ?>
<li class="page-item disabled">
<a class="page-link" href="#">上一页</a>
</li>
<?php endif; ?>
<!-- 页码 -->
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?php echo $i == $page ? 'active' : ''; ?>">
<a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a>
</li>
<?php endfor; ?>
<!-- 下一页 -->
<?php if ($page < $total_pages): ?>
<li class="page-item">
<a class="page-link" href="?page=<?php echo $page + 1; ?>">下一页</a>
</li>
<?php else: ?>
<li class="page-item disabled">
<a class="page-link" href="#">下一页</a>
</li>
<?php endif; ?>
</ul>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>场景:带搜索的分页
php
<?php
// 连接数据库
$conn = mysqli_connect('localhost', 'root', '', 'test');
// 获取搜索关键词
$keyword = isset($_GET['keyword']) ? $_GET['keyword'] : '';
// 设置分页参数
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$per_page = 10;
$start = ($page - 1) * $per_page;
// 构建查询条件
$where = '';
if ($keyword) {
$where = " WHERE username LIKE '%$keyword%' OR email LIKE '%$keyword%'";
}
// 查询总记录数
$sql = "SELECT COUNT(*) as total FROM users" . $where;
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
$total = $row['total'];
$total_pages = ceil($total / $per_page);
// 查询当前页的数据
$sql = "SELECT * FROM users" . $where . " ORDER BY id DESC LIMIT $start, $per_page";
$result = mysqli_query($conn, $sql);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户列表</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1>用户列表</h1>
<!-- 搜索表单 -->
<form method="GET" class="mb-4">
<div class="input-group">
<input type="text" name="keyword" class="form-control" placeholder="搜索用户名或邮箱" value="<?php echo $keyword; ?>">
<button type="submit" class="btn btn-primary">搜索</button>
</div>
</form>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>创建时间</th>
</tr>
</thead>
<tbody>
<?php while ($user = mysqli_fetch_assoc($result)): ?>
<tr>
<td><?php echo $user['id']; ?></td>
<td><?php echo $user['username']; ?></td>
<td><?php echo $user['email']; ?></td>
<td><?php echo $user['created_at']; ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
<!-- 分页导航 -->
<?php if ($total_pages > 1): ?>
<ul class="pagination justify-content-center">
<!-- 上一页 -->
<?php if ($page > 1): ?>
<li class="page-item">
<a class="page-link" href="?page=<?php echo $page - 1; ?>&keyword=<?php echo urlencode($keyword); ?>">上一页</a>
</li>
<?php else: ?>
<li class="page-item disabled">
<a class="page-link" href="#">上一页</a>
</li>
<?php endif; ?>
<!-- 页码 -->
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
<li class="page-item <?php echo $i == $page ? 'active' : ''; ?>">
<a class="page-link" href="?page=<?php echo $i; ?>&keyword=<?php echo urlencode($keyword); ?>"><?php echo $i; ?></a>
</li>
<?php endfor; ?>
<!-- 下一页 -->
<?php if ($page < $total_pages): ?>
<li class="page-item">
<a class="page-link" href="?page=<?php echo $page + 1; ?>&keyword=<?php echo urlencode($keyword); ?>">下一页</a>
</li>
<?php else: ?>
<li class="page-item disabled">
<a class="page-link" href="#">下一页</a>
</li>
<?php endif; ?>
</ul>
<?php endif; ?>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>总结
分页是 Web 应用程序中的常见功能,它可以提高页面加载速度,改善用户体验,减轻服务器负担。PHP 提供了多种实现分页的方法,包括基本的 SQL 查询和使用第三方库。
在开发过程中,应该注意以下几点:
- 合理设置每页显示的记录数
- 优化数据库查询,使用索引和 LIMIT 子句
- 生成分页导航,处理边界情况
- 支持搜索和排序功能
- 考虑 SEO 因素
通过合理实现分页功能,可以提高应用程序的性能和用户体验。
