優秀的分頁組件需滿足:
<?php
class Paginator {
private int $totalItems; // 總記錄數
private int $perPage; // 每頁條數
private int $currentPage; // 當前頁碼
private int $windowSize; // 顯示頁碼數量 (含首尾)
private string $urlPattern; // URL 模板,如 '/list?page=%d'
public function __construct(int $totalItems, int $perPage = 15, int $currentPage = 1, int $windowSize = 7, string $urlPattern = '') {
$this->totalItems = max(0, $totalItems);
$this->perPage = max(1, $perPage);
$this->currentPage = max(1, min($currentPage, $this->totalPages()));
$this->windowSize = max(3, $windowSize); // 至少首尾+當前
$this->urlPattern = $urlPattern ?: '?' . http_build_query(array_merge($_GET, ['page' => '%d']));
}
public function totalPages(): int {
return max(1, (int)ceil($this->totalItems / $this->perPage));
}
public function render(): string {
$total = $this->totalPages();
if ($total <= 1) return '';
$html = '';
return $html;
}
private function link(int $page, string $text, bool $active = false): string {
$url = sprintf($this->urlPattern, $page);
$class = $active ? ' class="active"' : '';
return "{$text} ";
}
// 獲取分頁數據 (用於控制器)
public function getLimitOffset(): array {
$offset = ($this->currentPage - 1) * $this->perPage;
return ['limit' => $this->perPage, 'offset' => $offset];
}
}
<?php
// ArticleController.php
public function index() {
$page = (int)($_GET['page'] ?? 1);
$perPage = 10;
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'root', '');
// 總數
$total = $pdo->query("SELECT COUNT(*) FROM articles WHERE status=1")->fetchColumn();
// 分頁器
$paginator = new Paginator($total, $perPage, $page, 7);
$limitOffset = $paginator->getLimitOffset();
// 數據查詢
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status=1 ORDER BY created_at DESC LIMIT :offset, :limit");
$stmt->bindValue(':offset', $limitOffset['offset'], PDO::PARAM_INT);
$stmt->bindValue(':limit', $limitOffset['limit'], PDO::PARAM_INT);
$stmt->execute();
$articles = $stmt->fetchAll();
// 視圖渲染
include 'article_list.php'; // 內含 = $paginator->render() ?>
}
.pagination ul {display: flex; gap: 4px; list-style: none; padding: 0;}
.pagination a {display: block; padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; color: #333; text-decoration: none;}
.pagination a:hover {background: #f5f5f5;}
.pagination .active a {background: #007bff; color: #fff; border-color: #007bff;}
.pagination .ellipsis {display: flex; align-items: center; padding: 0 8px; color: #999;}
| 算法 | 特點 | 適用場景 |
|---|---|---|
| 滑動窗口 (本文) | 當前頁居中、首尾固定、窗口大小恆定 | 通用後台、列表頁、SEO 友好 |
| 固定塊 | 每 10 頁一塊、跳轉塊 | 超大數據集 (百萬頁)、管理後台 |
| 簡化版 | 僅首尾、上下頁、當前頁±2 | 移動端、極簡 UI |
// JS 片段 (jQuery)
$('.pagination').on('click', 'a', function(e) {
e.preventDefault();
const url = $(this).attr('href');
$('#article-list').load(url + ' #article-list > *');
history.pushState(null, '', url);
});