Press "Enter" to skip to content

一个巧妙的分页方法

PHP是一个Web脚本语言,在Web应用中最常见的莫过于列表显示。所以页码生成也就犹为常用。 最近我索性写了一个,在生成思想上, 有一些技巧拿与大家分享:

先说说需求: 显示的页码数是$size, 当前的页数是$page, 总数是$total, 每页显示的条数是$page_size

首先,要获取总页面数是多少:

     $total_page = ceil($total/$page_size);
   

这样的写法是不是更简单一些呢?
其次,我是这样想的, 这个需求最高的需求是要根据当前的页码,找出size个页码数来。
那么就可以转化为: 根据当前的信息, 填充一个size个元素的数组。
最终就可以简单的转化为:根据当前的信息,寻找出size个元素的数组的起始元素是什么?我们假设这个起始元素是:page_start;

        $page_start = 1;
        $half       = intval($size/2);
        $page_start = max(1, $page - $half);
        $page_end   = min($page_start + $size - 1, $total_page);
        $page_start = max(1, $page_end - $size + 1);
   

这样写,是不是也很有技巧呢?
最后就简单了, 根据起始页和结束页, 生成一个数组, 最后foreach这个数组,生成html代码:

   $page_numbers = range($page_start, $page_end);
   $nav_str = "";
   foreach($page_numbers as $num){
      //.......
   }
  

源码:

/**
 *A pagination generation class
 *@class  : Pagination
 *@version: 1.0.0
 *@author : huixinchen at baidu.com
 *@useage:
 *      $pagi                   =  new Pagination($url_prefix, $page_size, $mesgs_count, $pagination_size=10, array $conf);
 *  $navigation_str = $pagi->generate($current_page_number);
 */
class Pagination{
        private $page, $total_page, $total, $page_size, $size;
        private $prev_str = "&lt", $next_str = "&gt";
        private $class, $selected_class = "selected", $prev_class="prev", $next_class="next",
                $de_prev_class="de_prev", $de_next_class="de_next";
        private $url_prefix="", $split_char="?";
        private $para_name = "page", $target = "";
        public function Pagination($url_prefix, $page_size, $total, $size=10, $conf=array()){
                $this->page       = 1;
                $this->page_size  = $page_size;
                $this->total      = $total;
                $this->total_page = intval(ceil($total/$page_size));
                $this->size               = $size;
                if(!empty($conf)){
                        $configure = array("prev_str", "next_str", "class", "selected_class");
                        foreach($conf as $key => $val){
                                if(in_array($key, $configure)){
                                        $this->$val = $val;
                                }
                        }
                }
                $this->url_prefix = $url_prefix;
                if(strstr($url_prefix, '?') !== false){
                        $this->url_prefix .= "&" . $this->para_name . "=";
                }else{
                        $this->url_prefix .= "?" . $this->para_name . "=";
                }
        }
        public function generate($page){
                $this->page = $page;
                if(isset($this->page[$page])){
                        return $this->page_str[$page];
                }
                $page_start = 1;
                $half           = intval($this->size/2);
                $page_start = max(1, $page - $half);
                $page_end       = min($page_start + $this->size - 1, $this->total_page);
                $page_start = max(1, $page_end - $this->size + 1);
                $this->page_str[$page] = $this->build_nav_str($page_start, $page_end);
                return $this->page_str[$page];
        }
        private function build_nav_str($page_start, $page_end){
                $page_nums = range($page_start, $page_end);
                $target    = $this->target? " target=\"{$this->target}\"" : "";
                if($this->page == 1){
                        $page_str = <<<html
                        <span class="{$this->de_prev_class}"> {$this->prev_str} </span>
HTML;
                }else{
                        $page     = $this->page - 1;
                        $page_str = <<<html
                        <span class="{$this->prev_class}"> <a href="{$this->url_prefix}{$page}"{$this->target}>{$this->prev_str}</a></span>
HTML;
                }
                foreach($page_nums as $p){
                        $page_str .= ($p == $this->page) ? <<<html
                        <span class="{$this->selected_class}">{$p}</span>
HTML
                        : <<<html
                        <span class="{$this->class}"><a href="{$this->url_prefix}{$p}"{$this->target}>{$p}</a></span>
HTML;
                }
                if($this->page == $this->total_page){
                        $page_str .= <<<html
                        <span class="{$this->de_next_class}"> {$this->next_str} </span>
HTML;
                }else{
                        $page      = $this->page + 1;
                        $page_str .= <<<html
                        <span class="{$this->next_class}"> <a href="{$this->url_prefix}{$page}"{$this->target}>{$this->next_str}</a></span>
HTML;
                }
                return $page_str;
        }
        public function tidy_str(){
                ;//void
        }
        public function __call($func_name, $arguments){
                if(isset($this->$func_name)){
                        return $this->$func_name;
                }
        }
        public function __destruct(){
                unset($this->page_str);
                unset($this);
        }
}
 

Be First to Comment

  1. Daemon
    Daemon August 31, 2016

    测试测试

  2. kitten flea control
    kitten flea control November 15, 2014

    Admiring the time and effort you put into your blog and
    in depth information you offer. It’s nice to come across a blog
    every once in a while that isn’t the same outdated rehashed
    information. Great read! I’ve bookmarked your site and
    I’m adding your RSS feeds to my Google account.

  3. 申皓方
    申皓方 September 24, 2013

    还有这句是不是也写错了?
    if(isset($this->page[$page])){
    return $this->page_str[$page];
    }
    应该是if(isset($this->page_str[$page]))这样的吧?

  4. 申皓方
    申皓方 September 24, 2013

    弱弱的问一句,这段代码里if里面的语句是不是写错了啊?
    $configure = array(“prev_str”, “next_str”, “class”, “selected_class”);
    foreach($conf as $key => $val){
    if(in_array($key, $configure)){
    $this->$val = $val;
    }
    }
    难道不是 $this->$key = $val;么?

  5. ts24
    ts24 September 18, 2013

    这个我以前也写过,现在看到完全熟悉的思路和陌生的变量名,感觉很奇妙呢。有些朋友对以下5行代码不是很明白,我帮忙解释一下吧
    $page_start = 1;
    $half = intval($size/2);/*获取显示页码数的一半,必须整*/
    $page_start = max(1, $page – $half);/*起始页码,不能小于1*/
    $page_end = min($page_start + $size – 1, $total_page);/*确定结束页码,不能大于最大页*/
    $page_start = max(1, $page_end – $size + 1);/*这是精华,若$size=10;$total_page=50;$page=49; 可避免显示44~50,而是更友好的显示41~50*/

  6. ZRJ
    ZRJ March 30, 2012

    这一段:
    $page_start = 1;
    $half = intval($size/2);
    $page_start = max(1, $page – $half);
    $page_end = min($page_start + $size – 1, $total_page);
    $page_start = max(1, $page_end – $size + 1);
    第五行不理解。。
    为什么需要再次计算$page_start呢?
    如果去掉第五行有什么问题呢?
    谢谢

  7. lonlee
    lonlee February 23, 2012

    可能是我比较笨吧,这5行代码,看了好几个小时没看懂。
    $page和$size有区别吗?如果都是代表当前页为什么要取2个变量呢?
    还有$half的这个值为什么要取一半呢?
    $page_start = 1;
    $half = intval($size/2);
    $page_start = max(1, $page – $half);
    $page_end = min($page_start + $size – 1, $total_page);
    $page_start = max(1, $page_end – $size + 1);
    你这5行代码能讲讲是用了什么思想呢。

  8. k
    k July 10, 2011

    鸟哥~~
    $page_str .= <<<HTML
    de_next_class}”> {$this->next_str}
    HTML;
    您这样写是因为效率还是为了整洁啊?
    这个好处是什么?

  9. MoontoC
    MoontoC May 12, 2010

    看第一句我就真经了,虽然还算实用,但是得到的东西是不是自己想要的还待考证
    $total_page = ceil($total/$page_size);
    ceil之类的普通数学函数属于很不严谨的一类做法,它们可以出现任何意想不到的问题,源于php的浮点精度差别,php文档下面的评论可以看到一些神奇的东西…
    我想说的是,数据的格式化不足

  10. xianbei
    xianbei March 26, 2010

    如果想写一个功能强大的分页类,那我建议写一个分页方面的框架吧,可以分为以下几个方面:
    1、主类:用来实现分页
    2、数据生成类:用来生成数据
    3、现实效果类:用来现实分页的效果,这个类支持插件,自己可以随便自定义现实效果
    4、插件类:用于其他一些功能
    当然可能还会有其他的一些分法和功能
    最重要的还是设计模式的良好运用

  11. zhj
    zhj August 14, 2009

    哎~这两天什么也没做,就研究你博客了。呵呵~~刚看了这篇,在找出$size个页码时我见过这种方法:
    $half = ceil($size/2);
    if($size-$half+$page > $total) {
    $half = $size-$total+$page;
    }
    $begin = $page – $half + 1;
    $begin = ($begin>=1)? $begin : 1;
    for($i=$begin;$i<$begin+$size;$i++) {
    if($i<=$total) {
    ….
    }
    }
    哎,本人才疏学浅,对于上面的和这个的思想都不是太了解,还请您指点~~不胜感激~

  12. ayiaman
    ayiaman January 3, 2009

    自己也写了一个,没这么复杂,传入
    $totalNum = $arr[‘totalNum’];
    $numPerPage = $arr[‘numPerPage’];
    $nowPage = $arr[‘nowPage’];
    $href = $arr[‘href’];
    这4个参数,自己返回分页的link。就43行代码

  13. cnangel
    cnangel December 1, 2008

    太复杂了,呵呵

  14. 无名
    无名 November 14, 2008

    速度啊,大哥,要下班了,给我说两下吧,今天看你的那个类都一天了,还是没有看明白,555

    • Vincent
      Vincent November 14, 2008

      我觉得你要关心的是这个思想, 不用太在意这个类。
      作者的本意,也只是一个思想么。

  15. 无名
    无名 November 14, 2008

    开始传参数时,$conf=array(),这个值开始时传什么呢? 还有就是,点击1,2,3,4,5的链接是,如何把值传传出去的呢?你的{$this->target}没法传值啊

    • 雪候鸟
      雪候鸟 November 15, 2008

      恩,这个conf是我留下做特殊定制的,一般不需要.

  16. 无名
    无名 November 14, 2008

    很多变量看不懂什么含义,一个方法这么多变量,不是很难用麽?还忘作者能吧各个变量什么意思,说明白。

    • 雪候鸟
      雪候鸟 November 14, 2008

      hi, 变量名的命名都是根据变量的实际意义来的,应该不难理解,;)

      • 无名
        无名 November 14, 2008

        大哥啊,你在线?能否给我说说 变量的意义,我看懂了类80%的东西,还差点就能用了 我QQ:492902285 方便聊下麽?

  17. Anonymous
    Anonymous November 10, 2008

    感觉只用做成render的一个插件函数就搞定, 这样调用过于麻烦

  18. jimmy
    jimmy November 4, 2008

    虽然我也大都不能理解这些文字,但我还是要来的...(决不是有意给本blog抹黑)

  19. yzcj007
    yzcj007 October 31, 2008

    我的想法是这样的,把页码生成的单独包一下(Core),把样式化页码的东东(涉及HTML的)写成一个包装器(Decoration),每次要用的时候包装下(eg.$page=new PageDecoration(new PageCore(args[0],args[1]…))),我觉得这样还蛮方便,算是逻辑与显示小分离了下,不知道搂主怎么看

  20. 玉面修罗
    玉面修罗 October 28, 2008

    我觉得写一个分页类不难,但是要写一个可扩展性及自定义功能极强的分页类就不容易了,至今还没找到非常好用的分页类。
    之前一直都是用别人写好的一个分页类, 在不同的项目中,产品人员总是会对分页效果提出各种各样的需求,所以每次都得把这个分页类
    拷贝一份,然后修改其中的HTML,修改样式,甚至修改和添加新的逻辑.总之觉得挺麻烦的,当然在类中可以把这些设计成可配置项,也可以
    用switch来根据参数进行不同类型的输出.总之感觉不能一处开发,多场合使用.我觉得好的分页类不应该只是一个类而已,应该还可以拆卸,,
    逻辑和表现都能分离到不同的层中,在表现层用多态,可以根据不同的场合自定义表现层的类.希望有这样的分页类,自己懒的写.

    • 雪候鸟
      雪候鸟 October 28, 2008

      恩,你说的很对。
      我这个其实主要还是介绍解决这样问题的一个方法
      最后的类,也只是我现在用的一个包装。 呵呵

    • ayiaman
      ayiaman November 3, 2008

      没必要什么扩展什么的,规范个格式,输入是$nowPage,$totalPage.
      输出就是一个有10个数字点击的分页。。

    • Rodin
      Rodin November 21, 2008

      老外有一个用策略模式做的分页:
      http://www.sitepoint.com/article/perfect-php-pagination/
      我觉得,把关键的分界输出(最后一页,当前页,其他都是扩展参数,比如上一页,下一页,上n页下n页),至于显示,交给smarty去做即可,甚至完全写成smarty的插件也行,在插件内部用smarty fetch动态获取分页模板

Leave a Reply

Your email address will not be published. Required fields are marked *