我们在项目开发的过程中避免不了使用分页功能,拿php来说,现在市面上有很多大大小小的php框架,当然了分页这种小功能这些框架中都是拿来直接可以用的。
这些框架的分页功能使用都很方便,配置一下分页所需参数立马就能出结果,对于开发人员来说是非常方便的。但是有些时候就会发现这些分页功能不是自己期望的,
当然拿框架的分页修改一下是可以实现我们的需求的,但是永远局限于框架本身的封装,那么我们怎么样定义自己的分页类呢,那么现在就要求我们不仅要知其然,更要知其所以然,
好了,废话那么多,咱们开始正题。
要实现分页功能,首先要知道数据总条数、每页显示的条数、显示几个分页码,这三个可谓是必要条件。
我们先看一下具体的实现效果
假设表结构是这样
CREATE TABLE `article_information` ( `inf_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `inf_title` varchar(50) NOT NULL DEFAULT '', `inf_smtitle` varchar(50) NOT NULL DEFAULT '', `inf_cid` int(10) unsigned NOT NULL, `inf_hits` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`inf_id`) ) ENGINE=MyISAM AUTO_INCREMENT=210 DEFAULT CHARSET=utf8;
首先我们连接数据库,php代码为:
$host = '127.0.0.1'; $db_user = 'root'; $db_pass = 'root'; $db_name = 'article'; $link = new mysqli($host,$db_user,$db_pass); if($link->errno){ printf("数据库链接失败:".mysqli_connect_error()); exit(); } if( ! $link->select_db($db_name)){ printf("数据库选择失败"); exit(); } $count_sql = "SELECT COUNT(1) nums FROM article_information";//查询数据总条数 $query = $link->query($count_sql); $row_count = $query->fetch_assoc(); $query->free_result(); $total = $row_count['nums']; $page_size = 10; $cur_page = get_url_param(parse_url($_SERVER['REQUEST_URI'])['path'],4); //假如url链接为,http://xxx.net/fenye.php/index/list/6?age=20&city=40 //我这边以平常开发常用的url链接为例,我这个链接只有两层index/list,如果是tp的话无路由链接一般为index/index/list(模块/控制器/方法)
get_url_param函数为:
/** * 获取url中的分段数据 * @param $url 链接url * @param $seg 获取url中的第几段 */ function get_url_param($url,$seg){ $url = explode('/',$url); return isset($url[$seg]) ? $url[$seg] : 1; }
接着继续,知道了总条数和每页显示的条数以后,下面我们开始取页码范围内的数据
$start = ($cur_page-1)*$page_size;//按照分页规律计算出数据起始条数 $sql = "SELECT inf_id,inf_title FROM py_information LIMIT $start,$page_size"; if ($result = $link->query($sql)){ $arr = []; while ($row = $result->fetch_assoc()){ $arr[] = $row; } }
现在我们已经把数据取出来了,下面我们开始写分页类。
下面是分页类的初始化配置:
具体配置参数我已经注释的很详细了,个别说明一下
$params参数:很多时候我们列表的分页会用到很多的查询参数,我经常看到各个框架论坛里面有同学问怎么把搜索参数带入到分页里面,那么这个参数就是让你把你的筛选参数组合到url中,格式为?id=20&city=30,
下面我们会讲如何将参数组合成这种格式的。
$base_url参数:当前链接(不带参数和页码),例如我们当前访问的地址为:http://xxx.net/fenye.php/index/list/6?age=20&city=40,那么$base_url =/fenye.php/index/list,我这是用原生php写的例子,如果用框架的话相关php文件是可以省略的。
class Mypage{ private $cur_page;//当前页 private $total;//总条数 private $page_size = 10;//每页显示的条数 private $total_page;//总页数 private $first_page;//首页显示名称 private $pre_page;//上一页的显示名称 private $nex_page;//下一页的显示名称 private $end_page;//尾页名称 private $params;//分页后面的筛选参数 private $num_size = 2;//当前页前后显示几个分页码 private $base_url;//分页链接地址 public function __construct(array $page_config=[]) { $this->cur_page = $page_config['cur_page']; $this->total = $page_config['total']; $this->page_size = $page_config['page_size']; $this->base_url = $page_config['base_url']; $this->pre_page = isset($page_config['pre_page']) ? $page_config['pre_page'] : "上一页"; $this->nex_page = isset($page_config['next_page']) ? $page_config['next_page'] : "下一页"; $this->end_page = isset($page_config['end_page']) ? $page_config['end_page'] : "尾页"; $this->first_page = isset($page_config['first_page']) ? $page_config['first_page'] : "首页"; $this->num_size = isset($page_config['num_size']) ? $page_config['num_size'] : 2; $this->params = isset($page_config['params']) ?$page_config['params'] : ''; $this->total_page = ceil($this->total/$this->page_size); }
}
配置好分页类,那么下一步我们就要获取分页码了,像下面图片中的分页样式一样。
我们来看一下一个分页地址需要哪些东西,假如第5页的链接地址为:
<a href="/fenye.php/index/list/5?age=20&city=40">5</a>
首先我们要组合第5页的链接地址/fenye.php/index/list/5?age=20&city=40,然后再返回<a href="/fenye.php/index/list/5?age=20&city=40">5</a>这个链接,
下面我们来具体实现
获取指定页码的地址:
/** * 获取分页地址 xxx.com/index/3 * @param $i */ public function get_url($i){ return $this->base_url.'/'.$i; }
返回整体页码链接,上面我们已经说了分页参数问题,这里$url.=$this->params就是对每一页的url后面都带上分页参数,这样就确保参数的正确性:
/** * 获取分页完整链接 * @param $url */ public function get_link($url,$text){ if ($this->params) $url.=$this->params; return "<a href='$url'>$text</a>"; }
有了上面两个函数,现在我们来获取一下第1页的页码链接,下面做了一个小小的判断,我想大家都能看明白,就是当前不是第一页就返回有链接的地址,否则只返回一个链接名称:
/** * 获取首页的链接地址 */ public function get_first_page(){ if ($this->cur_page > 1 && $this->cur_page != 1){ return $this->get_link($this->get_url(1),$this->first_page); } return "<span>$this->first_page</span>"; }
和上面类似,我们获取一个其它页码的链接地址:
/** * 获取上一页链接地址 */ public function get_prev_page(){ if ($this->cur_page > 1 && $this->cur_page !=1){ return $this->get_link($this->get_url($this->cur_page-1),$this->pre_page); } return '<span>'.$this->pre_page.'</span>'; } /** * 获取下一页链接地址 * @return string */ public function get_next_page(){ if ($this->cur_page < $this->total_page){ return $this->get_link($this->get_url($this->cur_page+1),$this->nex_page); } return '<span>'.$this->nex_page.'</span>'; } /** * 获取...符号 * @return string */ public function get_ext(){ return '<span>...</span>'; } /** * 获取尾页地址 */ public function get_end_page(){ if ($this->cur_page < $this->total_page){ return $this->get_link($this->get_url($this->total_page),$this->end_page); } return '<span>'.$this->end_page.'</span>'; }
还差一点,就是中间的数字页码,注释我已经写得很清楚了:
/** * 中间的数字分页 */ public function now_bar(){ if ($this->cur_page > $this->num_size){ $begin = $this->cur_page - $this->num_size; $end = $this->cur_page + $this->num_size; //判断最后一页是否大于总页数 if ($end > $this->total_page){ //重新计算开始页和结束页 $begin = ($this->total_page - 2*$this->num_size > 0) ? $this->total_page - 2*$this->num_size : 1; //这里为什么用2*$this->num_size呢?因为当前页前后有2个$this->num_size的间距,所以这里是2*$this->num_size $end = $this->total_page; } }else{ $begin = 1; $end = 2*$this->num_size+1;//此处的2和上面已经解释过了,+1是因为除了当前页,前后还有2*$this->num_size的间距,所以总页码条数为2*$this->num_size+1 } $page_html=''; for ($i=$begin;$i<=$end;$i++){ if ($i == $this->cur_page){ $page_html .= '<span class="disabled">'.$i.'</span>'; }else{ $page_html .= $this->get_link($this->get_url($i),$i); } } return $page_html; }
其它的两个函数
/** * 返回总条数 * @return string */ public function show_total_row(){ return "共{$this->total}条"; } /** * 返回总页数 */ public function show_total_page(){ return "共{$this->total_page}页"; }
最后输出分页:
/** * 输出分页码 */ public function show_page(){ $show_page = ''; $ext = ($this->cur_page>$this->num_size) ? $this->get_ext() : ''; $show_page.= $this->show_total_row(); $show_page.= $this->show_total_page(); $show_page.= $this->get_first_page(); $show_page.= $this->get_prev_page(); $show_page.= $ext; $show_page.= $this->now_bar(); $show_page.= $ext; $show_page.= $this->get_next_page(); $show_page.= $this->get_end_page(); return $show_page; }
以上就是分页的实现原理,下面我会给出具体代码,上面我们说过我们的查询参数组装成?id=20&city=30这种格式的来拼接到url后面,下面我们来看一下具体实现过程。
例如我们把分页条件都放进一个数组中:
$condition = [ 'age'=>20, 'city'=>40 ];
下面我们来进行url组合:
/** * 组合url参数 ?id=2&city=3 * @param array $data */ function make_url(array $data=[]){ $link = '?'; $suffix = '&'; foreach ($data as $key=>$val){ $link .= $key.'='.$val.$suffix; } return trim($link,$suffix); }
ok参数已经组合成我们所希望的格式了,其实就是一个foreach拼装数据的过程。
以上是一个原理和逻辑,下面给出所有代码:
完整分页类:
<?php /** * Created by PhpStorm. * User: 123456 * Date: 2018/9/4 * Time: 17:24 */ class Mypage{ private $cur_page;//当前页 private $total;//总条数 private $page_size = 10;//每页显示的条数 private $total_page;//总页数 private $first_page;//首页显示名称 private $pre_page;//上一页的显示名称 private $nex_page;//下一页的显示名称 private $end_page;//尾页名称 private $params;//分页后面的筛选参数 private $num_size = 2;//当前页前后显示几个分页码 private $base_url;//分页链接地址 public function __construct(array $page_config=[]) { $this->cur_page = $page_config['cur_page']; $this->total = $page_config['total']; $this->page_size = $page_config['page_size']; $this->base_url = $page_config['base_url']; $this->pre_page = isset($page_config['pre_page']) ? $page_config['pre_page'] : "上一页"; $this->nex_page = isset($page_config['next_page']) ? $page_config['next_page'] : "下一页"; $this->end_page = isset($page_config['end_page']) ? $page_config['end_page'] : "尾页"; $this->first_page = isset($page_config['first_page']) ? $page_config['first_page'] : "首页"; $this->num_size = isset($page_config['num_size']) ? $page_config['num_size'] : 2; $this->params = isset($page_config['params']) ?$page_config['params'] : ''; $this->total_page = ceil($this->total/$this->page_size); } /** * 获取首页的链接地址 */ public function get_first_page(){ if ($this->cur_page > 1 && $this->cur_page != 1){ return $this->get_link($this->get_url(1),$this->first_page); } return "<span>$this->first_page</span>"; } /** * 获取上一页链接地址 */ public function get_prev_page(){ if ($this->cur_page > 1 && $this->cur_page !=1){ return $this->get_link($this->get_url($this->cur_page-1),$this->pre_page); } return '<span>'.$this->pre_page.'</span>'; } /** * 获取下一页链接地址 * @return string */ public function get_next_page(){ if ($this->cur_page < $this->total_page){ return $this->get_link($this->get_url($this->cur_page+1),$this->nex_page); } return '<span>'.$this->nex_page.'</span>'; } /** * 获取...符号 * @return string */ public function get_ext(){ return '<span>...</span>'; } /** * 获取尾页地址 */ public function get_end_page(){ if ($this->cur_page < $this->total_page){ return $this->get_link($this->get_url($this->total_page),$this->end_page); } return '<span>'.$this->end_page.'</span>'; } /** * 中间的数字分页 */ public function now_bar(){ if ($this->cur_page > $this->num_size){ $begin = $this->cur_page - $this->num_size; $end = $this->cur_page + $this->num_size; //判断最后一页是否大于总页数 if ($end > $this->total_page){ //重新计算开始页和结束页 $begin = ($this->total_page - 2*$this->num_size > 0) ? $this->total_page - 2*$this->num_size : 1; //这里为什么用2*$this->num_size呢?因为当前页前后有2个$this->num_size的间距,所以这里是2*$this->num_size $end = $this->total_page; } }else{ $begin = 1; $end = 2*$this->num_size+1;//此处的2和上面已经解释过了,+1是因为除了当前页,前后还有2*$this->num_size的间距,所以总页码条数为2*$this->num_size+1 } $page_html=''; for ($i=$begin;$i<=$end;$i++){ if ($i == $this->cur_page){ $page_html .= '<span class="disabled">'.$i.'</span>'; }else{ $page_html .= $this->get_link($this->get_url($i),$i); } } return $page_html; } /** * 输出分页码 */ public function show_page(){ $show_page = ''; $ext = ($this->cur_page>$this->num_size) ? $this->get_ext() : ''; $show_page.= $this->show_total_row(); $show_page.= $this->show_total_page(); $show_page.= $this->get_first_page(); $show_page.= $this->get_prev_page(); $show_page.= $ext; $show_page.= $this->now_bar(); $show_page.= $ext; $show_page.= $this->get_next_page(); $show_page.= $this->get_end_page(); return $show_page; } /** * 获取分页地址 xxx.com/index/3 * @param $i */ public function get_url($i){ return $this->base_url.'/'.$i; } /** * 获取分页完整链接 * @param $url */ public function get_link($url,$text){ if ($this->params) $url.=$this->params; return "<a href='$url'>$text</a>"; } /** * 返回总条数 * @return string */ public function show_total_row(){ return "共{$this->total}条"; } /** * 返回总页数 */ public function show_total_page(){ return "共{$this->total_page}页"; } }
前台页面:
<?php include_once ('Mypage.php'); $host = '127.0.0.1'; $db_user = 'root'; $db_pass = 'root'; $db_name = 'article'; $link = new mysqli($host,$db_user,$db_pass); if (mysqli_connect_errno()){ printf("数据库链接失败了:".mysqli_connect_error()); exit(); } if ( ! $link->select_db($db_name)){ printf("你选择的数据库{$db_name}不存在"); exit(); } $page_size = 10; //p($url); //p(__DIR__); //p(__FILE__); //p(pathinfo(__FILE__, PATHINFO_BASENAME)); $condition = [ 'age'=>20, 'city'=>40 ]; //p(make_url($condition)); //$cur_page = isset($_GET['page']) ? $_GET['page'] : 1; $cur_page = get_url_param(parse_url($_SERVER['REQUEST_URI'])['path'],4); //假如url链接为,http://xxx.net/fenye.php/index/list/6?age=20&city=40 //我这边以平常开发常用的url链接为例,我这个链接只有两层index/list,如果是tp的话无路由链接一般为index/index/list(模块/控制器/方法) $sql = "SELECT COUNT(1) nums FROM py_information"; $query = $link->query($sql); $row = $query->fetch_assoc(); $total = $row['nums'];//数据总条数 $page_config=[ 'cur_page'=>$cur_page, 'total'=>$total, 'page_size'=>$page_size, 'base_url'=>'/fenye.php/index/list', 'num_link'=>2, 'params'=>make_url($condition) ]; $mypage = new Mypage($page_config); $start = ($cur_page-1)*$page_size;//按照分页规律计算出数据起始条数 $sql = "SELECT inf_id,inf_title FROM article LIMIT $start,$page_size"; if ($result = $link->query($sql)){ $arr = []; while ($row = $result->fetch_assoc()){ $arr[] = $row; } } /** * 获取url中的分段数据 * @param $url 链接url * @param $seg 获取url中的第几段 */ function get_url_param($url,$seg){ $url = explode('/',$url); return isset($url[$seg]) ? $url[$seg] : 1; } /** * 组合url参数 ?id=2&city=3 * @param array $data */ function make_url(array $data=[]){ $link = '?'; $suffix = '&'; foreach ($data as $key=>$val){ $link .= $key.'='.$val.$suffix; } return trim($link,$suffix); } function p($data){ echo '<pre>'; print_r($data); echo '</pre>'; }; ?> <html lang="en"> <head> <meta charset="utf-8"> <title>充值</title> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <style type="text/css"> .page_nav { font-family: Simsun; line-height:normal;text-align: right;margin-top: 10px;overflow: hidden;zoom: 1;text-align:center} .page_nav a,.page_nav span,.page_nav input{display:inline-block;line-height:23px;padding:0 10px;border:1px solid #ccc;background-color:#fff; text-decoration:none;color:#666;margin-right:5px;zoom: 1;} .page_nav input{height: 23px;line-height: 23px;padding: 0;zoom: 1; font:12px/16px;font-family: Simsun;zoom: 1;_margin-bottom:-4px;} .page_nav a:hover,.page_nav span.pg_curr{color:#fff !important;background-color:#C00;border-color:#be0d11 #be0d11 #9a0307; text-decoration:none;} .disabled{ background: #C00 !important; color: #fff !important;} </style> </head> <body> <table> <thead> <th>id</th> <th>标题</th> </thead> <?php foreach($arr as $key=>$val):?> <tr> <td><?php echo $val['inf_id'];?></td> <td><?php echo $val['inf_title'];?></td> </tr> <?php endforeach;?> </table> <div class="page_nav" style="margin: 15px;"> <?=$mypage->show_page();?> </div> </body> </html>
你可以将此分页类移植到tp或者CI等其它框架中去。
数据库你可以随便找一个,这里我就不提供了,感谢大家的阅读,有不对的地方请指正。
另外我在github上面开源了一些项目,觉得有用的给个star,地址:https://github.com/sunjiaqiang