分页显示是一种非常常见的浏览和显示大量数据的方法,属于web编程中最常处理的事件之一。对于web编程的老手来说,编写这种代码实在是和呼吸一样自然,但是对于初学者来说,常常对这个问题摸不着头绪,因此特地撰写此文对这个问题进行详细的讲解,力求让看完这篇文章的朋友在看完以后对于分页显示的原理和实现方法有所了解。本文适合初学者阅读,所有示例代码均使用php编写。
所谓分页显示,也就是将数据库中的结果集人为的分成一段一段的来显示,这里需要两个初始的参数:
- 每页多少条记录($PageSize)?
- 当前是第几页($CurrentPageID)?
现在只要再给我一个结果集,我就可以显示某段特定的结果出来。
至于其他的参数,比如:上一页($PreviousPageID)、下一页($NextPageID)、总页数($numPages)等等,都可以根据前边这几个东西得到。
以MySQL数据库为例,如果要从表内截取某段内容,sql语句可以用:select * from table limit offset, rows。看看下面一组sql语句,尝试一下发现其中的规率。
- select * from table limit 0,10 // 前10条记录
- select * from table limit 10,10 // 第11至20条记录
- select * from table limit 20,10 // 第21至30条记录
- ……
这一组sql语句其实就是当$PageSize=10的时候取表内每一页数据的sql语句,我们可以总结出这样一个模板:
- select * from table limit ($CurrentPageID - 1) * $PageSize, $PageSize
拿这个模板代入对应的值和上边那一组sql语句对照一下看看是不是那么回事。搞定了最重要的如何获取数据的问题以后,剩下的就仅仅是传递参数,构造合适的sql语句然后使用php从数据库内获取数据并显示了。以下我将用具体代码加以说明。
请详细阅读以下代码,自己调试运行一次,最好把它修改一次,加上自己的功能,比如搜索等等。
- <?php
- // 建立数据库连接
- $link = mysql_connect("localhost", "mysql_user", "mysql_password")
- or die("Could not connect: " . mysql_error());
- // 获取当前页数
- if( isset($_GET['page']) ){
- $page = intval( $_GET['page'] );
- }
- else{
- $page = 1;
- }
- // 每页数量
- $PageSize = 10;
- // 获取总数据量
- $sql = "select count(*) as amount from table";
- $result = mysql_query($sql);
- $row = mysql_fetch_row($result);
- $amount = $row['amount'];
- // 记算总共有多少页
- if( $amount ){
- if( $amount < $page_size ){ $page_count = 1; } //如果总数据量小于$PageSize,那么只有一页
- if( $amount % $page_size ){ //取总数据量除以每页数的余数
- $page_count = (int)($amount / $page_size) + 1; //如果有余数,则页数等于总数据量除以每页数的结果取整再加一
- }else{
- $page_count = $amount / $page_size; //如果没有余数,则页数等于总数据量除以每页数的结果
- }
- }
- else{
- $page_count = 0;
- }
- // 翻页链接
- $page_string = '';
- if( $page == 1 ){
- $page_string .= '第一页|上一页|';
- }
- else{
- $page_string .= '<a href="/?page=1>";第一页</a>|<a href="/?page='."($page-1).'>上一页</a>|';
- }
- if( ($page == $page_count) || ($page_count == 0) ){
- $page_string .= '下一页|尾页';
- }
- else{
- $page_string .= '<a href="/?page='."($page+1).'>下一页</a>|<a href="/?page='."$page_count.'>尾页</a>';
- }
- // 获取数据,以二维数组格式返回结果
- if( $amount ){
- $sql = "select * from table order by id desc limit ". ($page-1)*$page_size .", $page_size";
- $result = mysql_query($sql);
- while ( $row = mysql_fetch_row($result) ){
- $rowset[] = $row;
- }
- }else{
- $rowset = array();
- }
- // 没有包含显示结果的代码,那不在讨论范围,只要用foreach就可以很简单的用得到的二维数组来显示结果
- ?>
OO风格代码,以下代码中的数据库连接是使用的pear db类进行处理
- <?php
- // FileName: Pager.class.php
- // 分页类,这个类仅仅用于处理数据结构,不负责处理显示的工作
- Class Pager
- {
- var $PageSize; //每页的数量
- var $CurrentPageID; //当前的页数
- var $NextPageID; //下一页
- var $PreviousPageID; //上一页
- var $numPages; //总页数
- var $numItems; //总记录数
- var $isFirstPage; //是否第一页
- var $isLastPage; //是否最后一页
- var $sql; //sql查询语句
- function Pager($option)
- {
- global $db;
- $this->_setOptions($option);
- // 总条数
- if ( !isset($this->numItems) )
- {
- $res = $db->query($this->sql);
- $this->numItems = $res->numRows();
- }
- // 总页数
- if ( $this->numItems > 0 )
- {
- if ( $this->numItems < $this->PageSize ){ $this->numPages = 1; }
- if ( $this->numItems % $this->PageSize )
- {
- $this->numPages= (int)($this->numItems / $this->PageSize) + 1;
- }
- else
- {
- $this->numPages = $this->numItems / $this->PageSize;
- }
- }
- else
- {
- $this->numPages = 0;
- }
- switch ( $this->CurrentPageID )
- {
- case $this->numPages == 1:
- $this->isFirstPage = true;
- $this->isLastPage = true;
- break;
- case 1:
- $this->isFirstPage = true;
- $this->isLastPage = false;
- break;
- case $this->numPages:
- $this->isFirstPage = false;
- $this->isLastPage = true;
- break;
- default:
- $this->isFirstPage = false;
- $this->isLastPage = false;
- }
- if ( $this->numPages > 1 )
- {
- if ( !$this->isLastPage ) { $this->NextPageID = $this->CurrentPageID + 1; }
- if ( !$this->isFirstPage ) { $this->PreviousPageID = $this->CurrentPageID - 1; }
- }
- return true;
- }
- /***
- *
- * 返回结果集的数据库连接
- * 在结果集比较大的时候可以直接使用这个方法获得数据库连接,然后在类之外遍历,这样开销较小
- * 如果结果集不是很大,可以直接使用getPageData的方式获取二维数组格式的结果
- * getPageData方法也是调用本方法来获取结果的
- *
- ***/
- function getDataLink()
- {
- if ( $this->numItems )
- {
- global $db;
- $PageID = $this->CurrentPageID;
- $from = ($PageID - 1)*$this->PageSize;
- $count = $this->PageSize;
- $link = $db->limitQuery($this->sql, $from, $count); //使用Pear DB::limitQuery方法保证数据库兼容性
- return $link;
- }
- else
- {
- return false;
- }
- }
- /***
- *
- * 以二维数组的格式返回结果集
- *
- ***/
- function getPageData()
- {
- if ( $this->numItems )
- {
- if ( $res = $this->getDataLink() )
- {
- if ( $res->numRows() )
- {
- while ( $row = $res->fetchRow() )
- {
- $result[] = $row;
- }
- }
- else
- {
- $result = array();
- }
- return $result;
- }
- else
- {
- return false;
- }
- }
- else
- {
- return false;
- }
- }
- function _setOptions($option)
- {
- $allow_options = array(
- 'PageSize',
- 'CurrentPageID',
- 'sql',
- 'numItems'
- );
- foreach ( $option as $key => $value )
- {
- if ( in_array($key, $allow_options) && ($value != null) )
- {
- $this->$key = $value;
- }
- }
- return true;
- }
- }
- ?>
- <?php
- // FileName: test_pager.php
- // 这是一段简单的示例代码,前边省略了使用pear db类建立数据库连接的代码
- require "Pager.class.php";
- if ( isset($_GET['page']) )
- {
- $page = (int)$_GET['page'];
- }
- else
- {
- $page = 1;
- }
- $sql = "select * from table order by id";
- $pager_option = array(
- "sql" => $sql,
- "PageSize" => 10,
- "CurrentPageID" => $page
- );
- if ( isset($_GET['numItems']) )
- {
- $pager_option['numItems'] = (int)$_GET['numItems'];
- }
- $pager = @new Pager($pager_option);
- $data = $pager->getPageData();
- if ( $pager->isFirstPage )
- {
- $turnover = "首页|上一页|";
- }
- else
- {
- $turnover = "<a href='?page=1&numItems=".$pager->numItems."'>首页</a>|<a href="/?page=".$pager->PreviousPageID."&numItems=".$pager->numItems."'>上一页</a>|";
- }
- if ( $pager->isLastPage )
- {
- $turnover .= "下一页|尾页";
- }
- else
- {
- $turnover .= "<a href="/?page=".$pager->NextPageID."&numItems=".$pager->numItems."'>下一页</a>|<a href="/?page=".$pager->numPages."&numItems=".$pager->numItems."'>尾页</a>";
- }
- ?>
需要说明的地方有两个:
这个类仅仅处理数据,并不负责处理显示,因为我觉得将数据的处理和结果的显示都放到一个类里边实在是有些勉强。显示的时候情况和要求多变,不如自己根据类给出的结果处理,更好的方法是根据这个Pager类继承一个自己的子类来显示不同的分页,比如显示用户分页列表可以:
- <?php
- Class MemberPager extends Pager
- {
- function showMemberList()
- {
- global $db;
- $data = $this->getPageData();
- // 显示结果的代码
- // ......
- }
- }
- /// 调用
- if ( isset($_GET['page']) )
- {
- $page = (int)$_GET['page'];
- }
- else
- {
- $page = 1;
- }
- $sql = "select * from members order by id";
- $pager_option = array(
- "sql" => $sql,
- "PageSize" => 10,
- "CurrentPageID" => $page
- );
- if ( isset($_GET['numItems']) )
- {
- $pager_option['numItems'] = (int)$_GET['numItems'];
- }
- $pager = @new MemberPager($pager_option);
- $pager->showMemberList();
- ?>
第二个需要说明的地方就是不同数据库的兼容性,在不同的数据库里截获一段结果的写法是不一样的。
- mysql: select * from table limit offset, rows
- pgsql: select * from table limit m offset n
- ......
所以要在类里边获取结果的时候需要使用pear db类的limitQuery方法。
2 /**
3 *页面:datadisplay.php
4 *功能:分页显示数据,模仿Google的分页方法,可用于文章列表More页面或者后台数据管理等。
5 *方法:修改$dbhost,$dbusername$dbpassword,$dbname,$dbtablename为你运行环境的真实值
6 * 然后保存为php文件,在浏览器浏览执行该文件即可得到显示效果,如果像得到其它效果还可修改$pageSize等参数
7 *作者:十月冷风(Taoshuchen)
8 *OICQ: 2578549
9 *创建时间:2007-05-28
10 *版权:以上信息随意删除,知识来自互联网,只为分享经验心得,不存在任何版权问题。
11 */
12 $dbhost="host";//数据库地址
13 $dbusername="username";//链接数据库用户名
14 $dbpassword="password";//数据库密码
15 $dbname="dbname";//数据名称
16 $dbtablename="tablename";//数据表名
17 $conn = mysql_connect($dbhost,$dbusername,$dbpassword);//链接数据库(地址,数据库用户名,数据密码)
18 mysql_select_db($dbname,$conn)or die("链接数据库失败");//设置当前使用的数据库名
19 $sql_select="SELECT * FROM $dbtablename";
20 $rs=mysql_query($sql_select,$conn);
21 $totalRows=mysql_num_rows($rs);//总记录数
22 if ($totalRows>0) {//当然了,只有表中有数据才会用下面的一大堆程序来显示
23 $pageSize = 2; //每页显示的记录数
24 #当前页号$page,这个值由分页的链接得来,如果没有提供这个参数默认是显示第一页
25 if(!empty($_GET['page'])){
26 $page=$_GET['page'];
27 }else {
28 $page=1;
29 }
30 $totalPage = (int)ceil($totalRows/$pageSize); //总页数 总页数等于总记录数除以每页显示条数小数部分进一取整
31 #下面只是一些数据合法性的判断
32 if((int)$page<1){
33 $page=1;
34 }
35 if((int)$page > $totalPage){
36 $page=$totalPage;
37 }
38 #下面的加减2表示显示当前页前两页和后两页的,这个数字2可以根据你的需要修改,目的是为了像google那样只显示一定数量的分页链接,不至于在超大数据量分页时显示几十甚至上百个分页链接
39 #当然如果你一定要全都显示出来,只需要指定$begin=1;$end=$totalPage;即可
40 $begin = $page - 4;
41 $end = $page + 5;
42 #再下面还是一些数据合法性的判断
43 if($page<5){
44 $begin = 1;
45 $end = 10;//这里是你要提供的可用链接数,本例就是当前页加前后两页共5页,if条件为可用链接数的一半
46 }
47 if($page>$totalPage-5){
48 $begin = $totalPage - 9;
49 $end = $totalPage;
50 }
51 if($begin<1){
52 $begin = 1;
53 }
54 if($end>$totalPage){
55 $end = $totalPage;
56 }
57 #下面开始输出分页链接
58 /*==================显示第一种分页链接 开始===============*/
59 echo "<table border=1 bgcolor=\"gray\"><tr>";
60 echo "<td>$totalRows</td><td>$page/$totalPage</td>";
61 if($page>1){
62 echo "<td><a href=\"?page=1\"> |< </a></td><td><a href=\"?page=".($page-1)."\"> << </a></td>";
63 }
64 for($i=$begin; $i<=$end; $i++){
65 if($i==$page){
66 echo("<td bgcolor='red'> ");
67 }else{
68 echo("<td> ");
69 }
70 echo("<a href=\"?page={$i}\">$i</a>");
71 echo(" </td>");
72 }
73 if($page < $end){
74 echo "<td><a href=\"?page=".($page+1)."\"> >> </a></td><td><a href=\"?page=".$totalPage."\"> >| </a></td>";
75 }
76 echo "<td><input type=\"text\" size=\"3\" ōnkeydown=\"javascrīpt:if(event.keyCode==13){window.location='?page='+this.value;}\"></td>";
77 echo "</tr></table>";
78
79 /*==================显示第一种分页链接 结束===============*/
80
81 /*==================显示第二种分页链接 开始===============*/
82 echo "共有 $totalPage 页 $totalRows 条 <a href=\"?page=".($page-1)."\">< Back</a>";
83 #接下来的就是显示带有链接的1、2、3、4...5、6、7、8了
84 for($i=$begin; $i<=$end; $i++){
85 echo(" ");
86 echo("<a href=\"?page={$i}\">");
87 if($i==$page) echo("<font color=\"red\">");
88 echo("$i");
89 if($i==$page) echo("</font>");
90 echo("</a>");
91 echo(" ");
92 if($i!=$end) echo("|");
93 }
94 echo ("<a href=\"?page=".($page+1)."\">Next ></a>\n");
95 $selecet_page="<select name=\"page\" ōnChange=\"window.location='?page='+this.value;\">\n";
96 for($i=1;$i<=$totalPage;$i++){
97 if($i==$page){
98 $selecet_page.="<option value=\"$i\" selected>$i</option>\n";
99 }else {
100 $selecet_page.="<option value=\"$i\">$i</option>\n";
101 }
102 }
103 $selecet_page.="</select>\n";
104 echo $selecet_page;
105 /*==================显示第二种分页链接 结束===============*/
106 #链接显示完了下面就是显示数据了,当然了显示效果大家可以尽情发挥,这里只是提供一个通用的模式
107 #By the way:不论美工给你设么样效果页面,充分利用我们的 if for 绝对可以搞定
108
109 $beginNum=($page-1)*$pageSize;//查询记录从第几条开始
110 $sql_select="SELECT * FROM $dbtablename LIMIT $beginNum,$pageSize";
111 $rs=mysql_query($sql_select,$conn);
112 $rs_field_counts=mysql_num_fields($rs);//查询得到的字段数
113 echo ("<table border='1'>\n");
114 echo ("<tr bgcolor=\"#CCFFCC\">\n");
115 #显示表头,当然实际应用中不太可能直接输出数据库的字段名,那就把下面的for循环直接写成你要显示的表头就好了
116 for ($i=0;$i<$rs_field_counts;$i++){
117 $rs_field_name=mysql_field_name($rs,$i);
118 echo("<th>$rs_field_name</th>");
119 }
120 echo ("\n</tr>\n");
121 #显示数据
122 $row_number=0;//这个变量用于控制行显示颜色,
123 while ($row=mysql_fetch_array($rs)){
124 #判断是单数行还是偶数行,这个原理同样适用于每行显示两条记录的情况(带图片的产品展示一般会用到)
125 if ($row_number%2==0) {
126 $tr_color="#CCFFFF";
127 }else{
128 $tr_color="#CCFFCC";
129 }
130 echo ("<tr bgcolor=\"$tr_color\">\n");
131 for ($i=0;$i<$rs_field_counts;$i++){
132 echo ("<td>");
133 if(empty($row[$i])){
134 echo (" ");
135 }else {
136 echo ($row[$i]);
137 }
138 echo ("</td>");
139 }
140 echo ("\n</tr>\n");
141 $row_number++;
142 }
143 echo "</table>\n"; //不要忘了数据显示结束一定要输出这个呦!只有到这里你才算大功告成。
144 }else {//如果表中没有数据
145 echo("sorry,没有符合条件的数据!<br />\n");
146 }
147 //下面两句纯熟充数 :)
148 mysql_free_result($rs);
149 mysql_close($conn);
150 ?>