PHP 基本分頁技巧

作為一個網頁開發人員,你經常要用容易閱讀的格式把數據顯示給用戶,舉例說你要從數據庫讀取一份雇員名單,並在網頁上羅列出來,若果名單只有區區十 多人,用一個簡單迴圈把所有人列印出來便好了,不是很簡單嗎?但若果你有五十名雇員又如何呢?一百人?一千人?把這麼多人一口氣羅列出來顯然不是一個好主意,Crayon Violent 在 PHP Freaks 寫了一篇教學文件,介紹如何透過 PHP 來實作分頁。

同一時間從數據庫扯出所有數據,你的用戶可能會一面呆等一面納悶,系統究竟發什麼神經老是沒有輸出?千呼萬喚有結果了,得到的竟然是一本完完整整的「三國演義」,在一頁版面上列印出來!據說古人的確是把不論長短的文章寫在一匹絹布上,一種好像稱為卷軸的東西,詳情我也不清楚,但廿一世紀的你不會也這麼做吧?

聰明的做法是把一份長長的名單分割成很多小段,每次只從數據庫提取及顯示一段,這可以大幅降低伺服器的工作量,也提升網頁的下載速度,用戶也比較容易消化屏幕上的資料,不會一下子被你弄至消化不良,這種做法就是分頁。

一個基本的分頁函式初看起來可能又長又可怖,但其實你只要閉上眼睛,深吸一口氣,然後閱讀每一個分段,便會發現它其實是很簡單的東西。根據 Crayon 多年來在論壇上幫助別人的經驗,人們對分頁的最大困難在於搞不清這種技術叫作什麼!既然我們搞定了最困難的部份,餘下的便易如反掌了,不是嗎?

首先,我們要在數據庫中建立一個資料表,並放進一些數據,這裡不會討論安裝數據庫、建立資料表等細節,若果你不清楚這些東西,這份教學文件並不適合你。資料表的內容並不重要,若果你已經有一個滿載數據的資料表可作為測試用途,歡迎把以下程式中資料表和欄位的名稱更改,為了方便說明,文中的資料表名稱 是「numbers」,它有兩個欄位:「number」和「id」,兩者的類型都是 int,其中「id」是自動遞增的。

若果你打算用這些名稱自行建立資料表,不妨用以下的程式片段來產生測試用的數據:

 PHP |  copy code |? 
01
<?php
02
$conn = mysql_connect('localhost','dbusername','dbpassword') or trigger_error("SQL", E_USER_ERROR);
03
$db = mysql_select_db('dbname',$conn) or trigger_error("SQL", E_USER_ERROR);
04
 
05
for ($x = 0; $x < 106; $x++) {
06
    $number = rand(100,999);
07
    $sql = "INSERT INTO numbers (number, id) VALUES ($number, '')";
08
    $query = mysql_query($sql, $conn) or trigger_error("SQL", E_USER_ERROR);
09
}
10
?>

長話短說:這個程式會產生 106 個隨機的 3 位數,為什麼是 106 個呢?這只是隨便選的,而且,分頁程式每次會顯示 10 筆資料,這樣便可以保證最後一頁少於 10 筆資料。為什麼是 3 位數呢?為什麼最後一頁要顯示 6 筆資料?這些全部都是隨意選擇的,沒有特別的原因,請相信我。

好了,正式開始前我們先看看偉大的分頁程式完整版本,很多教學文件把程式解剖成一小段一小段,讀者們被逼重複多次的複製和貼上,十分令人困擾,但偏偏與本文的宗旨互相矛盾,不是很諷刺嗎?

 PHP |  copy code |? 
01
<?php
02
// 數據庫連結資料
03
$conn = mysql_connect('localhost','dbusername','dbpass') or trigger_error("SQL", E_USER_ERROR);
04
$db = mysql_select_db('dbname',$conn) or trigger_error("SQL", E_USER_ERROR);
05
 
06
// 計算資料表中有多少列
07
$sql = "SELECT COUNT(*) FROM numbers";
08
$result = mysql_query($sql, $conn) or trigger_error("SQL", E_USER_ERROR);
09
$r = mysql_fetch_row($result);
10
$numrows = $r[0];
11
 
12
// 每頁顯示的列數
13
$rowsperpage = 10;
14
// 計算總共需要多少頁
15
$totalpages = ceil($numrows / $rowsperpage);
16
 
17
// 取得當前的頁數,或者顯示預設的頁數
18
if (isset($_GET['currentpage']) && is_numeric($_GET['currentpage'])) {
19
    // 把變量的類型轉換成 int
20
    $currentpage = (int) $_GET['currentpage'];
21
} else {
22
    // 預設的頁數
23
    $currentpage = 1;
24
} // end if
25
 
26
// 若過當前的頁數大於頁數總數
27
if ($currentpage > $totalpages) {
28
    // 把當前頁數設定為最後一頁
29
    $currentpage = $totalpages;
30
} // end if
31
// 若果當前的頁數小於 1
32
if ($currentpage < 1) {
33
    // 把當前頁數設定為 1
34
    $currentpage = 1;
35
} // end if
36
 
37
// 根據當前頁數計算名單的起始位置
38
$offset = ($currentpage - 1) * $rowsperpage;
39
 
40
// 從數據庫取資料
41
$sql = "SELECT id, number FROM numbers LIMIT $offset, $rowsperpage";
42
$result = mysql_query($sql, $conn) or trigger_error("SQL", E_USER_ERROR);
43
 
44
// 若果還有資料的話...
45
while ($list = mysql_fetch_assoc($result)) {
46
    // 列印資料
47
    echo $list['id'] . " : " . $list['number'] . "<br />";
48
} // end while
49
 
50
/****** 建立分頁連結 ******/
51
// 顯示的頁數範圍
52
$range = 3;
53
 
54
// 若果正在顯示第一頁,無需顯示「前一頁」連結
55
if ($currentpage > 1) {
56
    // 使用 << 連結回到第一頁
57
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=1'><<</a> ";
58
    // 前一頁的頁數
59
    $prevpage = $currentpage - 1;
60
    // 使用 < 連結回到前一頁
61
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$prevpage'><</a> ";
62
} // end if
63
 
64
// 顯示當前分頁鄰近的分頁頁數
65
for ($x = (($currentpage - $range) - 1); $x < (($currentpage + $range) + 1); $x++) {
66
    // 如果這是一個正確的頁數...
67
    if (($x > 0) && ($x <= $totalpages)) {
68
        // 如果這一頁等於當前頁數...
69
        if ($x == $currentpage) {
70
            // 不使用連結, 但用高亮度顯示
71
            echo " [<b>$x</b>] ";
72
            // 如果這一頁不是當前頁數...
73
        } else {
74
            // 顯示連結
75
            echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$x'>$x</a> ";
76
        } // end else
77
    } // end if
78
} // end for
79
 
80
// 如果不是最後一頁, 顯示跳往下一頁及最後一頁的連結
81
if ($currentpage != $totalpages) {
82
    // 下一頁的頁數
83
    $nextpage = $currentpage + 1;
84
    // 顯示跳往下一頁的連結
85
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$nextpage'>></a> ";
86
    // 顯示跳往最後一頁的連結
87
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$totalpages'>>></a> ";
88
} // end if
89
/****** 完成建立分頁連結 ******/
90
?>

好了,就是這麼多代碼,它的基本構思是:

  • 計算你的資料表有多少列
  • 根據每頁你想顯示的列數,計算有多少分頁
  • 照出當前的頁數
  • 從數據庫提取需要顯示的資料
  • 顯示資料
  • 製作一些「第一頁」、「前一頁」、當前頁鄰近的分頁、「下一頁」和「最後一頁」的連結

上面的原碼有很多注釋,所以你應該可以弄清楚是怎麼回事,無論如何,下面我們會把它拆解分析,好,繼續前進……

 PHP |  copy code |? 
1
// 數據庫連結資料
2
$conn = mysql_connect('localhost','dbusername','dbpass') or trigger_error("SQL", E_USER_ERROR);
3
$db = mysql_select_db('dbname',$conn) or trigger_error("SQL", E_USER_ERROR); 

以上是幾行很顯然只是用來連接數據庫。

 PHP |  copy code |? 
1
// 計算資料表中有多少列
2
$sql = "SELECT COUNT(*) FROM numbers";
3
$result = mysql_query($sql, $conn) or trigger_error("SQL", E_USER_ERROR);
4
$r = mysql_fetch_row($result);
5
$numrows = $r[0]; 

知道了資料表有多少列,我們才知道有多少分頁,因此,我們執行基本的 count(*) 查詢並把接過儲存在 $numrows。

 PHP |  copy code |? 
1
// 每頁顯示的列數
2
$rowsperpage = 10;
3
// 計算總共需要多少頁
4
$totalpages = ceil($numrows / $rowsperpage); 

$rowsperpage 是每頁顯示的列數,我們把總列數($numrows)除以每頁的列數($rowsperpage),便知道有多少分頁需要顯示,由於不會有半頁(除非給你的愛犬啃掉了),我們使用 celi() 來計算確實的頁數。

 PHP |  copy code |? 
1
// 取得當前的頁數,或者顯示預設的頁數
2
if (isset($_GET['currentpage']) && is_numeric($_GET['currentpage'])) {
3
    // 把變量的類型轉換成 int
4
    $currentpage = (int) $_GET['currentpage'];
5
} else {
6
    // 預設的頁數
7
    $currentpage = 1;
8
} // end if 

當你點擊一個分頁的連結時,目標分頁的頁數會透過 GET 方法傳到伺服器,這裡正是要取得這個分頁頁數。由於第一次進入這個程式時並不會傳送這個變量,所以我們必須檢查流覽器是否傳來這個變量,與及它是否一個整數,說不定有人在 URL 手動更改這個變量的數值。

如果接收到這個變量,又是一個數字的話,我們便把它強制轉換為 int(整數)類型,原因是倘若有人在 URL 中把這個變量設定為 9.75,它便會轉換成第 9 分頁,除非你是哈利波特,否則應該很清楚世界上沒有第 9.75 分頁。

如果輸入的不是數字,我們預設為第一頁。

 PHP |  copy code |? 
01
// 若過當前的頁數大於總頁數...
02
if ($currentpage > $totalpages) {
03
&nbsp;&nbsp;&nbsp; // 把當前頁數設定為最後一頁
04
&nbsp;&nbsp;&nbsp; $currentpage = $totalpages;
05
} // end if
06
// 若果當前的頁數小於 1...
07
if ($currentpage < 1) {
08
&nbsp;&nbsp;&nbsp; // 把當前頁數設定為 1
09
&nbsp;&nbsp;&nbsp; $currentpage = 1;
10
} // end if 

下 一步是檢查這是否一個有效的頁碼,原因是倘若我們的小說只有 400 頁,有一個不滿小說結局,或者不肯相信小說就此結束的人,幻想我們還有一個隱藏起來的第 401 頁,於是直接在 URL 中把頁數更改為 401,那時怎麼辦?所以啊……如果有人輸入一個負數的頁數,或者超過總頁數的數字,我們分別把它預設為 1 和總頁數。

 PHP |  copy code |? 
1
// 根據當前頁數計算名單的起始位置
2
$offset = ($currentpage - 1) * $rowsperpage; 

我 們總共有 106 筆資料,但每次只顯示 10 筆,所以要計算從哪一筆資料開始提取,例如第 8 頁的起始列是第 80 筆資料,不過事實上應該是第 70 筆,因為電腦總愛從零開始計算,因此我們把目前的頁數(8)減去一,乘以每頁的列數(10)。再強調一次,電腦是從零開始計算的,所以:

第一頁我們將會顯示第 0 – 9 列

第二頁我們將會顯示第 10 – 19 列

……如此類推……

 PHP |  copy code |? 
1
 // 從數據庫取資料
2
$sql = "SELECT id, number FROM numbers LIMIT $offset, $rowsperpage";
3
$result = mysql_query($sql, $conn) or trigger_error("SQL", E_USER_ERROR);

現在執行 SQL 查詢讀取資料表,從指定的起始列數開始讀,提取 10 列數據,由於我們的資料表只有 106 列,最後一頁只有六列。

 PHP |  copy code |? 
1
// 若果還有資料的話...
2
while ($list = mysql_fetch_assoc($result)) {
3
    // 列印資料
4
    echo $list['id'] . " : " . $list['number'] . "<br />";
5
} // end while

 跟著我們利用一個簡單迴路顯示目前網頁的數據列,這裡沒有 table 或者 div 等花招,因為不是這篇文件的目的。下一步我們將建立導航欄…

 PHP |  copy code |? 
1
// 若果正在顯示第一頁,無需顯示「前一頁」連結
2
if ($currentpage > 1) {
3
    // 使用 << 連結回到第一頁
4
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=1'><<</a> ";
5
    // 前一頁的頁數
6
    $prevpage = $currentpage - 1;
7
    // 使用 < 連結回到前一頁
8
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$prevpage'><</a> ";
9
} // end if 

首 先要做的是製作一些類似「<< <」的東西,「 echo="" p="">

 PHP |  copy code |? 
01
// 顯示的頁數範圍
02
$range = 3;
03
// 顯示當前分頁鄰近的分頁頁數
04
for ($x = (($currentpage - $range) - 1); $x < (($currentpage + $range) + 1); $x++) {
05
    // 如果這是一個正確的頁數...
06
    if (($x > 0) && ($x <= $totalpages)) {
07
        // 如果這一頁等於當前頁數...
08
        if ($x == $currentpage) {
09
            // 不使用連結, 但用高亮度顯示
10
            echo " [<b>$x</b>] ";
11
            // 如果這一頁不是當前頁數...
12
        } else {
13
            // 顯示連結
14
            echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$x'>$x</a> ";
15
        } // end else
16
    } // end if
17
} // end for  

在這裡我們顯示一連串的網頁連結,它的概念是若果我們在第八頁,它會顯示:

<< < 5 6 7 [8] 9 10 11 > >> 

我們使用了 $range 代表頁數範圍,它代表目前頁數的左邊和右邊該顯示多少個連結(不是總數量),我們目前在第八頁,$range 是 3,所以 8 的左邊顯示 5 6 7,右邊顯示 9 10 11,迴路從目前頁數減 $range 開始,遍歷至目前頁數加 $range。在迴路中,我們首先檢查當前的頁數是否合法,例如我們正在第一頁,當然不想連結顯示為 -2 -1 0,下一件事就是檢查要顯示的頁數是否目前的頁數,是的話便把它加粗並用方括號令它突顯出來,我們也無須使它成為連結。若果當前的頁數合法,又不是目前的 頁數,便用這個數字做一個連結,就是這麼簡單。

 PHP |  copy code |? 
01
// 如果不是最後一頁, 顯示跳往下一頁及最後一頁的連結
02
if ($currentpage != $totalpages) {
03
    // 下一頁的頁數
04
    $nextpage = $currentpage + 1;
05
    // 顯示跳往下一頁的連結
06
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$nextpage'>></a> ";
07
    // 顯示跳往最後一頁的連結
08
    echo " <a href='{$_SERVER['PHP_SELF']}?currentpage=$totalpages'>>></a> ";
09
} // end if
10
/****** 完成建立分頁連結 ******/
11
?> 

這 部份負責製作「> >>」連結,方法與製作「<< <」如出一轍,只是方向相反罷了:如果我們不是在最後一頁,便需要製作「> >>」連結,製作「>」只需把目前頁數加一,至於「>>」則連結到 $totalpages 變量的值。

好了,製作分頁就只是這麼多東西,當然你可以加入一些花俏元素,例如加入一些按欄位排序的連結,天空才是你的極限!

祝大家編碼快樂! 

 

發表回覆

  

  

  

您可使用下列 these HTML標籤

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>