首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 分頁類深度解析:從核心算法到模板渲染、URL 構建與 SEO 友好實踐

     編輯:PHP 相關知識

分頁核心數學模型

總記錄數: $totalRows
每頁行數: $listRows
總頁數: $totalPages = ceil($totalRows / $listRows)
當前頁碼: $nowPage (1-based)
起始行號 (0-based): $firstRow = ($nowPage - 1) * $listRows
SQL LIMIT 子句: LIMIT $firstRow, $listRows

原始類代碼重構與增強

 '',
        'prev' => '
  • ', 'next' => '
  • ', 'first' => '
  • ««
  • ', 'last' => '
  • »»
  • ', 'page' => '
  • %d
  • ', 'current' => '
  • %d
  • ', 'disabled' => '
  • %s
  • ', 'ellipsis' => '
  • …
  • ', 'jump' => '
  • ', ]; public function __construct(int $totalRows = 0, int $listRows = 15) { $this->totalRows = max(0, $totalRows); $this->listRows = max(1, $listRows); $this->calculate(); } public function setTotalRows(int $totalRows): self { $this->totalRows = max(0, $totalRows); $this->calculate(); return $this; } public function setNowPage(int $page): self { $this->nowPage = max(1, min($page, $this->totalPages ?: 1)); $this->firstRow = ($this->nowPage - 1) * $this->listRows; return $this; } protected function calculate(): void { $this->totalPages = (int)ceil($this->totalRows / $this->listRows); if ($this->totalPages === 0) $this->totalPages = 1; $this->nowPage = max(1, min($this->nowPage, $this->totalPages)); $this->firstRow = ($this->nowPage - 1) * $this->listRows; } public function getLimit(): string { return "LIMIT {$this->firstRow}, {$this->listRows}"; } public function getOffset(): int { return $this->firstRow; } public function getLength(): int { return $this->listRows; } // URL 構建策略 protected function buildUrl(int $page): string { if ($this->urlPattern) { return str_replace('{page}', (string)$page, $this->urlPattern); } $params = $this->queryParams; $params['page'] = $page; if ($this->parameter) { parse_str($this->parameter, $extra); $params = array_merge($params, $extra); } return '?' . http_build_query($params); } public function render(): string { if ($this->totalRows === 0) return ''; $html = $this->config['header']; $halfRoll = (int)floor($this->rollPage / 2); $startPage = max(1, $this->nowPage - $halfRoll); $endPage = min($this->totalPages, $startPage + $this->rollPage - 1); if ($endPage - $startPage + 1 < $this->rollPage) { $startPage = max(1, $endPage - $this->rollPage + 1); } // 首頁 if ($this->nowPage > 1) { $html .= sprintf($this->config['first'], $this->buildUrl(1)); $html .= sprintf($this->config['prev'], $this->buildUrl($this->nowPage - 1)); } else { $html .= sprintf($this->config['disabled'], '««'); $html .= sprintf($this->config['disabled'], '«'); } // 省略號前 if ($startPage > 1) { $html .= sprintf($this->config['page'], $this->buildUrl(1), 1); if ($startPage > 2) $html .= $this->config['ellipsis']; } // 頁碼區 for ($i = $startPage; $i <= $endPage; $i++) { if ($i === $this->nowPage) { $html .= sprintf($this->config['current'], $i); } else { $html .= sprintf($this->config['page'], $this->buildUrl($i), $i); } } // 省略號後 if ($endPage < $this->totalPages) { if ($endPage < $this->totalPages - 1) $html .= $this->config['ellipsis']; $html .= sprintf($this->config['page'], $this->buildUrl($this->totalPages), $this->totalPages); } // 尾頁 if ($this->nowPage < $this->totalPages) { $html .= sprintf($this->config['next'], $this->buildUrl($this->nowPage + 1)); $html .= sprintf($this->config['last'], $this->buildUrl($this->totalPages)); } else { $html .= sprintf($this->config['disabled'], '»'); $html .= sprintf($this->config['disabled'], '»»'); } // 跳轉 if ($this->showPageJump && $this->totalPages > 1) { $html .= sprintf( $this->config['jump'], $this->buildUrl(1), // form action 指向首頁或當前頁 'page', // 隱藏字段名 (可自定義) $this->nowPage, $this->totalPages, $this->nowPage ); } $html .= $this->config['footer']; return $html; } // 靜態工廠方法 public static function fromQuery(int $totalRows, int $listRows = 15, string $pageParam = 'page'): self { $page = max(1, (int)($_GET[$pageParam] ?? 1)); $p = new self($totalRows, $listRows); $p->setNowPage($page); return $p; } }

    使用示例

    query("SELECT COUNT(*) FROM articles WHERE status=1")->fetchColumn();
    $paginator = Paginator::fromQuery($total, 20);
    $articles = $db->query("SELECT * FROM articles WHERE status=1 ORDER BY id DESC " . $paginator->getLimit())->fetchAll();
    
    // 視圖
    echo $paginator->render();
    
    // 自定義 URL 模式 (RESTful)
    $paginator->urlPattern = '/articles/page/{page}';
    $paginator->queryParams = ['category' => 'tech'];
    echo $paginator->render();
    
    // AJAX 分頁 (返回 JSON)
    if ($_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
        header('Content-Type: application/json');
        echo json_encode([
            'html' => $articleListHtml,
            'pagination' => $paginator->render(),
            'current_page' => $paginator->nowPage,
            'total_pages' => $paginator->totalPages,
        ]);
        exit;
    }
    

    SEO 友好最佳實踐

    策略實現效果
    語義化 HTML<nav aria-label="Pagination"><ul class="pagination">蜘蛛識別導航結構
    rel="prev/next"首頁/上一頁/下一頁/尾頁鏈接添加 rel="prev" / rel="next"Google 理解分頁系列,合併索引信號
    Canonical 標準化首頁 <link rel="canonical" href="/list">,分頁頁 href="/list/page/2"避免重複內容懲罰
    避免 JS 渲染分頁服務端直出 HTML,或 SSR/預渲染確保蜘蛛可抓取所有頁碼鏈接
    載入更多/無限滾動配合 IntersectionObserver,但保留傳統分頁作為降級體驗與 SEO 平衡

    大數據量優化:游標分頁 vs 偏移分頁

    -- 偏移分頁 (深分頁性能劣化)
    SELECT * FROM t ORDER BY id LIMIT 1000000, 20; -- 掃描 100萬行丟棄
    
    -- 游標分頁 (Keyset Pagination, 適合無限滾動/API)
    SELECT * FROM t WHERE id > :last_seen_id ORDER BY id LIMIT 20; -- 索引命中,極快
    -- 需唯一、有序、不變的列 (主鍵、創建時間+ID)
    
    • 偏移分頁:適合頁碼跳轉、總頁數可見、中小數據量
    • 游標分頁:適合無限滾動、大數據量、API 分頁、不需總頁數
    • 混合策略:前 1000 頁用偏移,超出引導用戶用搜索/篩選

    結語

    分頁類是 Web 應用中最高頻復用組件之一。優秀的分頁類應具備:核心算法正確、URL 構建靈活、模板可定制、SEO 語義化、大數據量優化策略、零依賴易集成。本文重構版採用配置驅動模板、策略模式構建 URL、工廠方法簡化實例化,可直接投入生產或作為框架分頁組件的參考實現。記住:分頁不僅是切片數據,更是用戶導航、蜘蛛爬取、系統性能的關鍵交匯點。

    1. 上一篇文章:
    2. 下一篇文章:
    Copyright © 程式碼之家 All Rights Reserved