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

PHP 模板分頁類:邏輯與視圖分離、支持自定義標簽、AJAX 無刷新

     編輯:PHP 相關知識

設計理念

分頁組件應將「數據邏輯」(總數、每頁條數、當前頁、SQL LIMIT)與「視圖渲染」(HTML 結構、CSS 類、鏈接格式)完全分離。本文實現一個 Pager 類:

  • 構造函數接收基礎參數
  • setTotal($count) 設置總記錄數
  • getLimit() 返回 SQL LIMIT 子句
  • render($templateFile) 讀取模板文件,替換標簽輸出 HTML
  • 模板採用簡單標簽語法:{PAGE_FIRST}、{PAGE_PREV}、{PAGE_LIST}、{PAGE_NEXT}、{PAGE_LAST}、{PAGE_CURRENT}、{PAGE_TOTAL}

核心類實現

<?php
class Pager {
    // 配置
    public int $currentPage = 1;
    public int $pageSize = 15;
    public int $linkCount = 5;           // 數字鏈接顯示個數(奇數最佳)
    public string $urlPattern = '?page={page}'; // {page} 會被替換
    public string $ajaxTarget = '';      // 若設置,輸出 data-page 屬性供 JS 攔截

    // 運行時數據
    private int $totalRecords = 0;
    private int $totalPages = 0;

    public function __construct(array $options = []) {
        foreach ($options as $k => $v) if (property_exists($this, $k)) $this->$k = $v;
        $this->currentPage = max(1, (int)$this->currentPage);
        $this->pageSize = max(1, (int)$this->pageSize);
        $this->linkCount = max(3, (int)$this->linkCount | 1); // 確保奇數
    }

    public function setTotal(int $count): self {
        $this->totalRecords = max(0, $count);
        $this->totalPages = (int)ceil($this->totalRecords / $this->pageSize);
        $this->currentPage = min($this->currentPage, max(1, $this->totalPages));
        return $this;
    }

    /** 返回 SQL LIMIT 子句 */
    public function getLimit(): string {
        $offset = ($this->currentPage - 1) * $this->pageSize;
        return "LIMIT $offset, {$this->pageSize}";
    }

    /** 獲取頁碼範圍 */
    private function getPageRange(): array {
        $half = ($this->linkCount - 1) / 2;
        $start = max(1, $this->currentPage - $half);
        $end = min($this->totalPages, $start + $this->linkCount - 1);
        if ($end - $start + 1 < $this->linkCount) {
            $start = max(1, $end - $this->linkCount + 1);
        }
        return [$start, $end];
    }

    /** 渲染模板文件 */
    public function render(string $tplFile): string {
        if (!is_file($tplFile)) throw new RuntimeException("模板不存在: $tplFile");
        $tpl = file_get_contents($tplFile);

        // 標簽替換映射
        $tags = [
            'PAGE_CURRENT' => $this->currentPage,
            'PAGE_TOTAL'   => $this->totalPages,
            'PAGE_SIZE'    => $this->pageSize,
            'RECORD_TOTAL' => $this->totalRecords,
        ];

        // 首頁/尾頁
        $tags['PAGE_FIRST'] = $this->buildLink(1, '首頁', $this->currentPage === 1);
        $tags['PAGE_LAST']  = $this->buildLink($this->totalPages, '尾頁', $this->currentPage === $this->totalPages);

        // 上一頁/下一頁
        $tags['PAGE_PREV'] = $this->buildLink($this->currentPage - 1, '上一頁', $this->currentPage === 1);
        $tags['PAGE_NEXT'] = $this->buildLink($this->currentPage + 1, '下一頁', $this->currentPage === $this->totalPages);

        // 數字鏈接列表
        [$start, $end] = $this->getPageRange();
        $listHtml = '';
        for ($i = $start; $i <= $end; $i++) {
            $listHtml .= $this->buildLink($i, (string)$i, $i === $this->currentPage);
        }
        $tags['PAGE_LIST'] = $listHtml;

        // 替換
        $search = []; $replace = [];
        foreach ($tags as $k => $v) {
            $search[] = '{' . $k . '}';
            $replace[] = $v;
        }
        return str_replace($search, $replace, $tpl);
    }

    private function buildLink(int $page, string $text, bool $disabled): string {
        if ($disabled) {
            return "$text";
        }
        $url = str_replace('{page}', $page, $this->urlPattern);
        $attr = $this->ajaxTarget ? " data-page=\"$page\" data-target=\"{$this->ajaxTarget}\"" : '';
        return "$text";
    }
}
?>

模板文件 pager.tpl

<nav class="pagination" aria-label="Page navigation">
    {PAGE_FIRST}
    {PAGE_PREV}
    {PAGE_LIST}
    {PAGE_NEXT}
    {PAGE_LAST}
    <span class="page-info">共 {RECORD_TOTAL} 條 / {PAGE_TOTAL} 頁,當前第 {PAGE_CURRENT} 頁</span>
</nav>

使用示例

<?php
// 1. 獲取總數(示例使用 PDO)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pwd');
$total = $pdo->query("SELECT COUNT(*) FROM articles WHERE status=1")->fetchColumn();

// 2. 實例化分頁
$pager = new Pager([
    'currentPage' => $_GET['page'] ?? 1,
    'pageSize'    => 10,
    'linkCount'   => 7,
    'urlPattern'  => 'article_list.php?page={page}',
    // 'ajaxTarget' => '#article-list', // 啟用 AJAX 模式
]);
$pager->setTotal($total);

// 3. 查詢當前頁數據
$stmt = $pdo->prepare("SELECT id, title, created_at FROM articles WHERE status=1 ORDER BY id DESC " . $pager->getLimit());
$stmt->execute();
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);

// 4. 輸出列表
foreach ($articles as $art) {
    echo "

{$art['title']}

"; } // 5. 輸出分頁導航 echo $pager->render(__DIR__ . '/templates/pager.tpl'); ?>

AJAX 無刷新分頁(前端 10 行代碼)

<script>
document.addEventListener('click', e => {
    const a = e.target.closest('a[data-page]');
    if (!a) return;
    e.preventDefault();
    const target = document.querySelector(a.dataset.target);
    if (!target) return;
    fetch(a.href, {headers: {'X-Requested-With': 'XMLHttpRequest'}})
        .then(r => r.text())
        .then(html => target.innerHTML = html);
});
</script>

後端檢測 $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest' 時只輸出列表 HTML 片段(不含分頁導航),或輸出 JSON {listHtml: '...', pagerHtml: '...'} 由前端分別填充。

總結

本分頁類通過「模板文件 + 標簽替換」實現視圖與邏輯解耦,支持自定義 URL 模式、鏈接數量、AJAX 掛載,代碼精簡、無第三方依賴,易於集成到任何 PHP 項目(原生、Laravel、ThinkPHP、Symfony 等)。

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