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

PHP 分頁顯示完整實作:從 SQL LIMIT 到分頁類封裝與前端樣式

     編輯:PHP 相關知識

分頁核心邏輯:三個關鍵參數

所有分頁系統只需三個輸入:

  1. 每頁筆數 $pageSize (如 10、20、50)
  2. 當前頁碼 $currentPage (從 URL $_GET['page'] 取得,預設 1)
  3. 總資料筆數 $totalRows (執行 SELECT COUNT(*) 取得)

由此衍生:

  • 總頁數 $totalPages = ceil($totalRows / $pageSize)
  • 資料庫 OFFSET $offset = ($currentPage - 1) * $pageSize
  • SQL 查詢 SELECT * FROM table ORDER BY id DESC LIMIT $offset, $pageSize

MySQL LIMIT 語法詳解

-- 格式:LIMIT [offset,] rows
-- offset 從 0 開始 (第 1 筆為 0)

-- 第 1 頁 (每頁 10 筆)
SELECT * FROM articles LIMIT 0, 10;      -- 或 LIMIT 10

-- 第 2 頁
SELECT * FROM articles LIMIT 10, 10;

-- 第 3 頁
SELECT * FROM articles LIMIT 20, 10;

-- 通用模板
SELECT * FROM table ORDER BY id DESC LIMIT (page-1)*pageSize, pageSize;
⚠ 效能警示: LIMIT 1000000, 10 (深分頁) 會掃描前 100 萬行再丟棄,極慢。百萬級資料請用「游標分頁」或「延遲關聯」優化:
SELECT * FROM table WHERE id > last_seen_id ORDER BY id LIMIT 10 (需前端配合傳遞最後一筆 ID)

完整可用分頁類 Paginator (PHP 7.4+)

<?php
// Paginator.php
class Paginator {
    public int $currentPage = 1;
    public int $pageSize = 15;
    public int $totalRows = 0;
    public int $totalPages = 0;
    public int $offset = 0;
    public string $urlPattern = '?page={page}'; // {page} 會被替換
    public array $queryParams = []; // 額外查詢參數 ['keyword' => 'php', 'category' => 3]

    public function __construct(int $pageSize = 15) {
        $this->pageSize = $pageSize;
        $this->currentPage = max(1, (int)($_GET['page'] ?? 1));
    }

    public function setTotalRows(int $totalRows): self {
        $this->totalRows = max(0, $totalRows);
        $this->totalPages = (int)ceil($this->totalRows / $this->pageSize);
        $this->currentPage = min($this->currentPage, max(1, $this->totalPages));
        $this->offset = ($this->currentPage - 1) * $this->pageSize;
        return $this;
    }

    // 取得 LIMIT 子句字串
    public function getLimitClause(): string {
        return "LIMIT {$this->offset}, {$this->pageSize}";
    }

    // 產生分頁連結 URL
    public function getPageUrl(int $page): string {
        $params = array_merge($this->queryParams, ['page' => $page]);
        return '?' . http_build_query($params);
    }

    // 判斷是否有上/下一頁
    public function hasPrev(): bool { return $this->currentPage > 1; }
    public function hasNext(): bool { return $this->currentPage < $this->totalPages; }

    // 取得顯示的頁碼範圍 (如 1 ... 4 5 [6] 7 8 ... 20)
    public function getPageRange(int $delta = 2): array {
        $start = max(1, $this->currentPage - $delta);
        $end = min($this->totalPages, $this->currentPage + $delta);
        // 保證至少顯示 2*delta+1 頁 (若總頁數足夠)
        if ($end - $start < 2 * $delta) {
            if ($start == 1) $end = min($this->totalPages, $start + 2 * $delta);
            elseif ($end == $this->totalPages) $start = max(1, $end - 2 * $delta);
        }
        return range($start, $end);
    }

    // 渲染 Bootstrap 5 風格分頁 HTML
    public function render(): string {
        if ($this->totalPages <= 1) return '';
        $html = '';
        return $html;
    }
}

控制器使用範例 (PDO + Paginator)

<?php
// ArticleController.php
require 'Paginator.php';
require 'pdo_connect.php'; // 回傳 $pdo

$pager = new Paginator(10); // 每頁 10 筆

// 1. 取得總筆數 (可加搜尋條件)
$where = "WHERE status = 1";
$params = [];
if (!empty($_GET['keyword'])) {
    $where .= " AND title LIKE ?";
    $params[] = "%{$_GET['keyword']}%";
    $pager->queryParams['keyword'] = $_GET['keyword']; // 保留分頁連結參數
}

$stmt = $pdo->prepare("SELECT COUNT(*) FROM articles {$where}");
$stmt->execute($params);
$pager->setTotalRows((int)$stmt->fetchColumn());

// 2. 取得當頁資料
$stmt = $pdo->prepare("SELECT * FROM articles {$where} ORDER BY created_at DESC " . $pager->getLimitClause());
$stmt->execute($params);
$articles = $stmt->fetchAll();

// 3. 傳遞給視圖
include 'article_list_view.php';

視圖檔 article_list_view.php

<!DOCTYPE html>
<html><head>
    <meta charset="utf-8">
    <title>文章列表</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container py-4">
    <h1 class="mb-4">文章列表</h1>
    
    <!-- 搜尋表單 (保留分頁參數) -->
    <form method="get" class="row g-2 mb-4">
        <div class="col-auto">
            <input type="text" name="keyword" class="form-control" placeholder="搜尋標題..." value="<?= htmlspecialchars($_GET['keyword'] ?? '') ?>">
        </div>
        <div class="col-auto">
            <button class="btn btn-primary">搜尋</button>
        </div>
    </form>

    <!-- 文章列表 -->
    <div class="table-responsive">
        <table class="table table-striped table-hover">
            <thead><tr><th>ID</th><th>標題</th><th>作者</th><th>建立時間</th></tr></thead>
            <tbody>
                <?php foreach ($articles as $art): ?>
                <tr>
                    <td><?= $art['id'] ?></td>
                    <td><?= htmlspecialchars($art['title']) ?></td>
                    <td><?= htmlspecialchars($art['author_name'] ?? '-') ?></td>
                    <td><?= $art['created_at'] ?></td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>

    <!-- 分頁導航 -->
    <?= $pager->render() ?>
</body></html>

進階優化:大數據量深分頁解決方案

方案原理適用場景前端配合
游標分頁WHERE id > last_id ORDER BY id LIMIT 10無限滾動、時間軸、日誌傳遞 last_id 而非 page
延遲關聯先查 SELECT id FROM t LIMIT offset, 10,再 SELECT * FROM t WHERE id IN (...)需保留跳頁功能的大表維持 page 參數
覆蓋索引建立 INDEX (status, created_at, id) 讓 COUNT 與 LIMIT 走索引所有分頁查詢無
預估總數EXPLAIN SELECT ... 取 rows 估值,或 Redis 緩存總數極大表 (億級),精確總數非必須顯示「約 10,000 筆」

結語

分頁看似簡單,實則包含 **SQL LIMIT 機制、參數驗證、URL 狀態保持、深分頁效能、前端無障礙** 等多層面考量。本文提供的 Paginator 類別封裝了核心邏輯,支援 Bootstrap 5 輸出、搜尋參數保留、頁碼範圍智慧顯示,可直接納入專案或依需求擴充 (如 AJAX 分頁、游標分頁介面)。記住:**永遠驗證 $currentPage 範圍、使用預處理防注入、針對大表優化索引與查詢策略**,即能構建穩健高效的分頁系統。

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