分頁本質上是對數據集進行切片。核心參數只有三個:總記錄數 ($total)、當前頁碼 ($page)、每頁條數 ($num)。由此推導出所有顯示所需數據:
ceil($total / $num)($page - 1) * $num$offset + 1 (若 $total > 0)min($offset + $num, $total)採用封裝性設計,構造函數初始化計算,對外僅暴露 getPageInfo() 返回關聯陣列,便於模板引擎直接渲染。
<?php
/**
* 簡易分頁計算類
* 職責:純粹計算分頁參數,不輸出 HTML,不依賴數據庫
*/
class Page
{
private $total; // 總記錄數
private $page; // 當前頁碼
private $num; // 每頁條數
private $pageNum; // 總頁數
private $offset; // SQL LIMIT 偏移量
/**
* 構造函數
* @param int $total 總記錄數 (必填)
* @param int $page 當前頁碼 (默認 1)
* @param int $num 每頁條數 (默認 10)
*/
public function __construct($total, $page = 1, $num = 10)
{
$this->total = max(0, (int)$total);
$this->num = max(1, (int)$num);
$this->page = max(1, (int)$page);
$this->pageNum = $this->calculatePageNum();
// 修正頁碼超出範圍
if ($this->page > $this->pageNum && $this->pageNum > 0) {
$this->page = $this->pageNum;
}
$this->offset = $this->calculateOffset();
}
private function calculatePageNum()
{
return (int)ceil($this->total / $this->num);
}
private function calculateOffset()
{
return ($this->page - 1) * $this->num;
}
/**
* 獲取完整分頁信息陣列,供模板使用
* @return array
*/
public function getPageInfo()
{
return [
'row_total' => $this->total,
'row_num' => $this->num,
'page_num' => $this->pageNum,
'current_page' => $this->page,
'row_offset' => $this->offset, // SQL LIMIT offset
'next_page' => ($this->page < $this->pageNum) ? $this->page + 1 : false,
'prev_page' => ($this->page > 1) ? $this->page - 1 : false,
'page_start' => ($this->total > 0) ? $this->offset + 1 : 0, // 顯示用起始序號
'page_end' => min($this->offset + $this->num, $this->total), // 顯示用結束序號
];
}
}
?>
<?php
// 1. 獲取總數 (Model 層)
$total = $db->query("SELECT COUNT(*) FROM article")->fetchColumn();
// 2. 實例化分頁類
$currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$pageSize = 15; // 每頁 15 條
$page = new Page($total, $currentPage, $pageSize);
$pageInfo = $page->getPageInfo();
// 3. 獲取當前頁數據 (Model 層)
$offset = $pageInfo['row_offset'];
$limit = $pageInfo['row_num'];
$articles = $db->query("SELECT * FROM article ORDER BY id DESC LIMIT $offset, $limit")->fetchAll();
// 4. 分配給模板
$tpl->assign('articles', $articles);
$tpl->assign('page', $pageInfo);
$tpl->display('article_list.tpl');
?>
分頁類不輸出 HTML,由模板控制顯示樣式,實現邏輯與視圖分離。
{* Smarty 模板片段 *}
{* 總數信息 *}
共 {$page.row_total} 條,第 {$page.current_page}/{$page.page_num} 頁
{* 首頁/上一頁 *}
{if $page.prev_page}
首頁
上一頁
{else}
首頁
上一頁
{/if}
{* 頁碼列表 (簡易版:顯示前後 2 頁) *}
{for $i = max(1, $page.current_page-2) to min($page.page_num, $page.current_page+2)}
{if $i == $page.current_page}
{$i}
{else}
{$i}
{/if}
{/for}
{* 下一頁/尾頁 *}
{if $page.next_page}
下一頁
尾頁
{else}
下一頁
尾頁
{/if}
LIMIT offset, num 深分頁性能急劇下降。解決方案:游標分頁 (Keyset Pagination) — 記住上一頁最後一條記錄的 ID,查詢 WHERE id > last_id ORDER BY id LIMIT num。?keyword=php&status=1&page=2),可用 http_build_query(array_merge($_GET, ['page' => $p])) 生成。page=1;添加 <link rel="prev" href="..."> / rel="next" 標籤。COUNT(*) 可緩存至 Redis (過期時間 1-5 分鐘),減輕數據庫壓力。一個優秀的分頁類應職責單一:只負責數學計算,不操作數據庫、不輸出 HTML、不依賴全局變量。Page 類通過構造函數注入參數,暴露 getPageInfo() 返回標準陣列,極易於單元測試和模板渲染,是現代 PHP 開發中分頁邏輯的標準實現方式。