10.6 分页原理及实现
对于WEB应用程序而言,最常见的功能是从数据库表中查询信息然后显示到WEB页面上。如果数据库表中的数据量大,从数据库表中查询数据并在 WEB 页面中进行显示,无疑会增加数据库服务器、应用服务器以及网络的负担,并为浏览器用户浏览数据带来不便。解决这一问题最常用的方法是使用分页技术。
10.6.1 分页原理
分页是一种将所有信息分段展示给浏览器用户的技术。浏览器用户每次看到的不是全部信息,而是其中的一部分信息,如果没有找到自己想要的内容,用户可以通过指定的页码或翻页的方式转换可见内容,直到找到自己想要的内容为止。在 B/S 三层架构中,从浏览器发送请求数据到WEB服务器返回响应数据的整个过程如图10-21所示,从图中可以得知,基于B/S三层架构的分页技术可以分别在浏览器、WEB服务器或数据库服务器实现。
方案1在浏览器端实现分页
浏览器端可以使用 JavaScript 代码实现分页功能,但前提是从数据库中查询满足条件的所有记录,将记录集先发送到WEB服务器,再从WEB服务器发送到浏览器,然后由浏览器JavaScript代码实现数据过滤。特点:效率最低,消耗大量服务器资源和网络资源。
方案2在WEB服务器端实现分页
WEB服务器端可以使用应用程序实现分页功能,但前提是从数据库中查询满足条件的所有记录,将记录集先发送到 WEB 服务器,然后由应用程序过滤该结果集,筛选出用户需要的“记录集”后,再发送到浏览器。特点:效率较低,消耗一定的服务器资源和网络资源。
方案3在数据库服务器端实现分页
数据库服务器端可以使用SQL语句实现分页功能,直接将用户所需记录集发送到WEB服务器,再发送到浏览器端即可,无需 WEB 服务器和浏览器过滤。特点:效率较高,消耗最少的服务器资源和网络资源。这里我们使用该方案实现分页技术。
10.6.2 PHP分页的最简单实现
不管使用哪种分页方案,程序员需要设置每页多少条记录($page_size),例如$page_size = 3。另外浏览器用户需要指定要访问第几页的数据,即当前是第几页($page_current),通常 URL 中提供了该信息,例如news_list.php?page_current=2。
在MySQL数据库服务器端实现分页需要使用MySQL中的谓词limit,语法格式如下:
limit [start,]length;
length的值等于$page_size变量的值,start的值可由$page_current和$page_size两个变量推算得出:($page_current-1)*$page_size。
将news_list.php程序中“get_connection();”与“close_connection();”之间的代码修改为如下代码(粗体字部分为代码的改动部分,其他代码不变)。
……
<?php
get_connection();
//分页的实现
$page_size = 3;
if(isset($_GET["page_current"])){
$page_current = $_GET["page_current"];
}else{
$page_current=1;
}
$start = ($page_current-1)*$page_size;
$result_sql = "select * from news order by news_id desc limit $start,$page_size";
if(isset($_GET["keyword"])){
$keyword = $_GET["keyword"];
//构造模糊查询新闻的SQL语句
$result_sql = "select * from news where title like '%$keyword%' or content like '%$keyword%' order by news_id desc limit $start,$page_size";
}
$result_set = mysql_query($result_sql);
close_connection();
……
在浏览器地址栏中输入地址“http://localhost/news/news/news_list.php?page_current=1”,浏览器将显示第一页的3条记录,以此类推。
10.6.3 带有“分页导航条”分页的实现
为了方便浏览器用户更好地使用分页功能,通常需要程序员定制一个“分页导航条”($navigator)方便浏览器用户翻页,如图10-22所示。
图 10-22 中的“分页导航条”($navigator)模仿了“百度搜索引擎”分页导航条,该分页导航条除了包含前面介绍的两个信息外,还包含了以下信息。
(1)共多少条记录($total_records):该信息可以使用SQL语句“select * from table_name”和PHP函数mysql_num_rows()获取(或使用SQL语句“select count(*) from table_name”和PHP函数mysql_fetch_array()获取)。
(2)总共多少页($total_pages):$total_pages可由ceil($total_records/$page_size)计算得出。
ceil ()函数语法格式:float ceil (float value)
ceil ()函数功能:返回不小于 value 的下一个整数,value如果有小数部分则进一位。
(3)上一页($page_previous):该信息可由下面的方法计算得出。
$page_previous = ($page_current<=1)?1:$page_current-1;
(4)下一页($page_next):该信息可由下面的代码段计算得出。
$page_next = ($page_current>=$total_pages)?$total_pages:$page_current+1;
$page_next = ($page_next==0)?1:$page_next; //没有记录时,$page_next的最小值为1
(5)设置$navigator 变量存储分页导航条字符串信息,$navigator的值可由下面的方法计算得出。
$url = $_SERVER['PHP_SELF'];
$navigator = "<a href=$url?page_current=$page_previous>上一页</a> ";
$page_start = ($page_current-5>0)?$page_current-5:0;
$page_end = ($page_start+10<$total_pages)?$page_start+10:$total_pages;
$page_start = $page_end-10;
if($page_start<0) $page_start = 0;
for($i=$page_start;$i<$page_end;$i++){
$j = $i+1;
$navigator.="<a href='$url?page_current=$j'>$j</a> ";
}
$navigator.="<a href=$url?page_current=$page_next>下一页</a><br/>";
$navigator.= "共".$total_records."条记录,共".$total_pages."页,当前是第".$page_current."页";
所有的有关分页导航条信息准备完毕后,只需将$navigator 信息打印出来就可以显示如图10-23所示的分页导航条。
10.6.4 分页函数的制作
对于任意的 WEB 系统而言,分页功能是最常用的功能之一,有必要将分页功能的代码封装为分页函数,便于代码维护和重用。在“C:\wamp\www\news\functions\”目录下创建page.php文件,在page.php文件中定义一个分页函数page(),该函数实现的功能是打印分页导航条。page函数需要5个输入参数,分别是:$total_records、$page_size、$page_current、$url和$keyword,这些参数的含义请参考前面的内容。page.php程序代码如下。
<?php
function page($total_records,$page_size,$page_current,$url,$keyword){
$total_pages = ceil($total_records/$page_size);
$page_previous = ($page_current<=1)?1:$page_current-1;
$page_next = ($page_current>=$total_pages)?$total_pages:$page_current+1;
$page_next = ($page_next==0)?1:$page_next;
$page_start = ($page_current-5>0)?$page_current-5:0;
$page_end = ($page_start+10<$total_pages)?$page_start+10:$total_pages;
$page_start = $page_end-10;
if($page_start<0) $page_start = 0;
if(empty($keyword)){
$navigator = "<a href=$url?page_current=$page_previous>上一页</a> ";
for($i=$page_start;$i<$page_end;$i++){
$j = $i+1;
$navigator.="<a href='$url?page_current=$j'>$j</a> ";
}
$navigator.="<a href=$url?page_current=$page_next>下一页</a>";
$navigator.= "<br/>共".$total_records."条记录,共".$total_pages."页,当前是第".$page_current."页";
}else{
$keyword = $_GET["keyword"];
$navigator = "<a href=$url?keyword=$keyword&page_current=$page_previous>上一页</a> ";
for($i=$page_start;$i<$page_end;$i++){
$j = $i+1;
$navigator.="<a href='$url?keyword=$keyword&page_current=$j'>$j</a> ";
}
$navigator.="<a href=$url?keyword=$keyword&page_current=$page_next>下一页</a>";
$navigator.= "<br/>共".$total_records."条记录,共".$total_pages."页,当前是第".$page_current."页";
}
echo $navigator;
}
?>
为了实现带有分页导航条的新闻标题列表显示页面,只需在news_list.php程序中调用page()函数,并向该函数传递5个参数即可。将news_list.php程序修改为如下代码(粗体字部分为代码的改动部分,其他代码不变)。
<?php
include_once("functions/database.php");
include_once("functions/page.php");
//显示文件上传的状态信息
if(isset($_GET["message"])){
echo $_GET["message"]."<br/>";
}
……
<?php
get_connection();
//分页的实现
$result_news = mysql_query($search_sql);
$total_records = mysql_num_rows($result_news);
$page_size = 3;
…
</table>
<?php
$url = $_SERVER["PHP_SELF"];
page($total_records,$page_size,$page_current,$url,$keyword);
?>
使用同样的方法将 review_list.php 程序修改为如下代码,实现新闻评论浏览的分页显示(粗体字部分为代码的改动部分,其他代码不变)。
<?php
include_once("functions/database.php");
include_once("functions/page.php");
$sql = "select * from review";
get_connection();
//分页的实现
$result_news = mysql_query($sql);
$total_records = mysql_num_rows($result_news);
$page_size = 3;
if(isset($_GET["page_current"])){
$page_current = $_GET["page_current"];
}else{
$page_current = 1;
}
$start = ($page_current-1)*$page_size;
$result_sql = "select * from review order by review_id desc limit $start,$page_size";
$result_set = mysql_query($result_sql);
close_connection();
echo "新闻发布系统的所有评论信息如下:<br/>";
while($row = mysql_fetch_array($result_set)){
echo "评论内容:".$row["content"]."<br/>";
……
}
//分页的实现
$url = $_SERVER["PHP_SELF"];
page($total_records,$page_size,$page_current,$url,"");
?>