%upPage%、%linkPage% 等佔位符)<?php
class Page {
public $rollPage = 6; // 分頁欄顯示頁碼數
public $parameter = ''; // 額外 URL 參數字串
public $listRows = 20; // 每頁筆數
public $firstRow = 0; // 起始行數 (LIMIT offset)
protected $totalPages = 0; // 總頁數
protected $totalRows = 0; // 總筆數
protected $nowPage = 1; // 當前頁
protected $coolPages = 0; // 分頁組數
protected $config = [
'redirect' => false, // 是否顯示跳轉表單
'header' => '條記錄', // 總筆數標籤
'prev' => '上一頁',
'next' => '下一頁',
'first' => '1',
'last' => '最後一頁',
'theme' => '<div class="pagination">%upPage% %first% %prePage% %linkPage% %nextPage% %downPage%</div> <div class="pagination-info">共 %totalPage% 頁</div>'
];
protected $varPage = 'p'; // 分頁參數名
// ...
}
?>
<?php
public function __construct($totalRows, $listRows = '', $parameter = '') {
$this->totalRows = (int)$totalRows;
$this->parameter = $parameter;
$this->varPage = defined('VAR_PAGE') ? VAR_PAGE : 'p'; // 支援常數覆寫
if ($listRows) $this->listRows = (int)$listRows;
$this->totalPages = ceil($this->totalRows / $this->listRows);
$this->coolPages = ceil($this->totalPages / $this->rollPage);
// 當前頁驗證
$this->nowPage = isset($_GET[$this->varPage]) ? max(1, (int)$_GET[$this->varPage]) : 1;
if ($this->totalPages > 0 && $this->nowPage > $this->totalPages) {
$this->nowPage = $this->totalPages;
}
$this->firstRow = $this->listRows * ($this->nowPage - 1); // LIMIT 起始位置
}
?>
<?php
public function show() {
if ($this->totalRows === 0) return '';
// 1. 處理 URL 參數:移除分頁參數,保留其他
$p = $this->varPage;
$url = $_SERVER['REQUEST_URI'] . (strpos($_SERVER['REQUEST_URI'], '?') ? '' : '?') . $this->parameter;
$parse = parse_url($url);
if (isset($parse['query'])) {
parse_str($parse['query'], $params);
unset($params[$p]);
$url = $parse['path'] . '?' . http_build_query($params);
}
// 2. 計算顯示頁碼範圍
if ($this->totalPages <= $this->rollPage) {
$start = 1; $end = $this->totalPages;
} else {
if ($this->nowPage <= $this->rollPage - 1) {
$start = 1; $end = $this->rollPage; $isLast = true;
} elseif ($this->nowPage > $this->totalPages - $this->rollPage + 1) {
$start = $this->totalPages - $this->rollPage + 1; $end = $this->totalPages; $isFirst = true;
} else {
$size = (int)($this->rollPage / 2);
$start = $this->nowPage - $size; $end = $this->nowPage + $size;
$isFirst = $isLast = true;
}
}
// 3. 組裝各區塊 HTML
$upRow = $this->nowPage - 1;
$downRow = $this->nowPage + 1;
$theFirst = $isFirst ? "<a class='first' href='{$url}&{$p}=1'>{$this->config['first']}</a>" : '';
$theEnd = $isLast ? "<a class='last' href='{$url}&{$p}={$this->totalPages}'>{$this->config['last']}</a>" : '';
$upPage = $upRow > 0 ? "<a class='prev' href='{$url}&{$p}={$upRow}'>{$this->config['prev']}</a>" : '';
$downPage = $downRow <= $this->totalPages ? "<a class='next' href='{$url}&{$p}={$downRow}'>{$this->config['next']}</a>" : '';
$linkPage = '';
if ($start == 3) $linkPage .= "<a href='{$url}&{$p}=2'>2</a>";
if ($start >= 4) $linkPage .= "<a href='{$url}&{$p}=2'>2</a><span class='ellipsis'>...</span>";
for ($i = $start; $i <= $end; $i++) {
if ($i != $this->nowPage) {
$linkPage .= " <a href='{$url}&{$p}={$i}'>{$i}</a>";
} else {
$linkPage .= " <span class='current'>{$i}</span>";
}
if ($i == $end && $i < $this->totalRows) {
$linkPage .= " <span class='ellipsis'>...</span>";
}
}
// 4. 替換主題佔位符
$pageStr = str_replace(
['%header%','%nowPage%','%totalRow%','%totalPage%','%upPage%','%first%','%prePage%','%linkPage%','%nextPage%','%downPage%','%end%'],
[$this->config['header'],$this->nowPage,$this->totalRows,$this->totalPages,$upPage,$theFirst,'',$linkPage,'',$downPage,$theEnd],
$this->config['theme']
);
// 5. 可選跳轉表單
if ($this->config['redirect'] && $this->totalPages > 1) {
$redirect = "<form method='get' action=''>到第 <input name='{$p}' type='text' size='3' maxlength='3' value='{$this->nowPage}'/> 頁 <input type='submit' value='確定'/>";
if (!empty($params)) {
foreach ($params as $k => $v) {
$redirect .= "<input type='hidden' name='{$k}' value='{$v}'/>";
}
}
$redirect .= "</form>";
$pageStr .= $redirect;
}
return $pageStr;
}
?>
<?php
// 1. 取得總筆數
$total = $pdo->query('SELECT COUNT(*) FROM articles WHERE status=1')->fetchColumn();
// 2. 實例化分頁類
$page = new Page($total, 15, '&category=tech&sort=desc'); // 每頁 15 筆,保留分類/排序參數
$page->setConfig('theme', '<nav aria-label="Page navigation">%upPage% %linkPage% %downPage%</nav>');
$page->setConfig('prev', '«');
$page->setConfig('next', '»');
$page->setConfig('redirect', true); // 啟用跳轉表單
// 3. 查詢當頁資料
$offset = $page->firstRow;
$limit = $page->listRows;
$articles = $pdo->query("SELECT * FROM articles WHERE status=1 ORDER BY id DESC LIMIT $offset, $limit")->fetchAll();
// 4. 模板輸出
?>
<div class="articles">
<?php foreach ($articles as $art): ?>
<article><h2><?= htmlspecialchars($art['title']) ?></h2></article>
<?php endforeach; ?>
</div>
<?= $page->show() ?>
.pagination { display: flex; gap: 4px; align-items: center; margin: 16px 0; }
.pagination a, .pagination span { padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; text-decoration: none; color: #333; }
.pagination a:hover { background: #f5f5f5; }
.pagination .current { background: #007bff; color: #fff; border-color: #007bff; }
.pagination .ellipsis { border: none; color: #999; }
.pagination-info { color: #666; font-size: 0.9em; }
.pagination form { display: inline-flex; gap: 4px; align-items: center; margin-left: 16px; }
.pagination input[type=text] { width: 40px; text-align: center; }
這個 `Page` 類提供生產級分頁功能:參數自動保持、主題高度可配、跳轉表單、SEO 標記友善。核心邏輯集中於 `show()` 方法的 URL 處理、頁碼範圍計算與模板替換,易於理解與二次開發。實務中建議搭配快取總數、游標分頁等優化策略應對大數據場景。