構建一個完整的分頁列表頁,包含資料查詢、分頁邏輯、頁碼導航生成,並採用現代 PHP 最佳實踐(PDO、預處理語句、關注點分離)。
CREATE TABLE `persons` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`first_name` VARCHAR(50) NOT NULL,
`last_name` VARCHAR(50) NOT NULL,
`email` VARCHAR(100),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
<?php
class Paginator
{
private PDO $pdo;
private int $perPage;
private string $baseUrl;
public function __construct(PDO $pdo, int $perPage = 10, string $baseUrl = '')
{
$this->pdo = $pdo;
$this->perPage = $perPage;
$this->baseUrl = $baseUrl ?: $_SERVER['PHP_SELF'];
}
public function paginate(string $table, array $conditions = [], array $params = []): array
{
$page = max(1, (int)($_GET['page'] ?? 1));
$offset = ($page - 1) * $this->perPage;
// 構建 WHERE 子句
$whereSql = '';
if ($conditions) {
$whereSql = 'WHERE ' . implode(' AND ', $conditions);
}
// 總筆數查詢
$countSql = "SELECT COUNT(*) FROM `{$table}` {$whereSql}";
$stmt = $this->pdo->prepare($countSql);
$stmt->execute($params);
$total = (int)$stmt->fetchColumn();
// 分頁數據查詢
$dataSql = "SELECT * FROM `{$table}` {$whereSql} ORDER BY `id` DESC LIMIT {$offset}, {$this->perPage}";
$stmt = $this->pdo->prepare($dataSql);
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return [
'data' => $rows,
'pagination' => $this->buildPagination($total, $page),
];
}
private function buildPagination(int $total, int $currentPage): array
{
$totalPages = (int)ceil($total / $this->perPage);
$range = 3; // 當前頁前後顯示頁碼數
$start = max(1, $currentPage - $range);
$end = min($totalPages, $currentPage + $range);
$pages = [];
if ($currentPage > 1) {
$pages[] = ['page' => 1, 'label' => '首頁', 'active' => false];
$pages[] = ['page' => $currentPage - 1, 'label' => '上一頁', 'active' => false];
}
for ($i = $start; $i <= $end; $i++) {
$pages[] = ['page' => $i, 'label' => (string)$i, 'active' => $i === $currentPage];
}
if ($currentPage < $totalPages) {
$pages[] = ['page' => $currentPage + 1, 'label' => '下一頁', 'active' => false];
$pages[] = ['page' => $totalPages, 'label' => '尾頁', 'active' => false];
}
return [
'current' => $currentPage,
'total' => $totalPages,
'count' => $total,
'per_page' => $this->perPage,
'links' => $pages,
];
}
}
<?php
require_once 'Paginator.php';
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
$paginator = new Paginator($pdo, 5);
$result = $paginator->paginate('persons');
$rows = $result['data'];
$pagination = $result['pagination'];
?>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>用戶列表</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
.pagination { display: flex; gap: 5px; justify-content: center; margin-top: 20px; }
.pagination a, .pagination span { padding: 8px 12px; border: 1px solid #ddd; text-decoration: none; }
.pagination .active { background: #007bff; color: white; border-color: #007bff; }
</style>
</head>
<body>
<h1>用戶列表 (共 筆)</h1>
<table>
<thead>
<tr><th>ID</th><th>姓名</th><th>Email</th><th>建立時間</th></tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<div class="pagination">
<span class="active"></span>
<a href="baseUrl . '?page=' . $link['page']; ?>">
</a>
</div>
</body>
</html>
Paginator 類,視圖模板純粹負責渲染。htmlspecialchars() 轉義。此架構易於擴展為 API 介面(返回 JSON)、加入快取層、或整合至 Laravel、Symfony 等框架的分頁組件中。