Skip to content

Digg Style Pagination

Derek Jones edited this page Jul 5, 2012 · 10 revisions

Category:Libraries

Digg Style Pagination Library

The existing pagination class is sufficient, however, I wanted to produce digg-style pagination.

See here for an example of what I mean - http://www.strangerstudios.com/sandbox/pagination/diggstyle.php. A lot of the updated code has been taken from the script provided there. However, some other improvements/alterations have also been made. Basically this is a mish-mash between the CI_Pagination library and the code found at the above link.

Benefits

  • ability to let the user control how many records per page they would like to see. The url's for this are in the form of /controller/function/perpage/page. E.g. /controller/function/10/2 means show 10 records per page, and show the second page. Therefore you would be getting records 11- 20. Make sense?
  • pages are in terms of page numbers (e.g. /view/10/2 means page 2 rather than offset of 2 which is more logical to me)
  • if there are 100 pages (for example) rather than showing only a few pages either side of this in the pagination, digg-style will show a few pages either side as well as the first couple of pages and the last couple of pages. I think this increases usability.
  • i think it looks better :)

I have only overwritten the existing CI_Pagination class. This is a bit lazy but if you want to do the right thing you should probably rename it and call it as a custom-made library. This is my first library and I am not a guru PHP coder so there may well be something which can be optimised further or some minor bugs. Test it thoroughly before using in anything critical.

Ok first of all, the updated /system/libraries/Pagination.php file.

<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
* Code Igniter
*
* An open source application development framework for PHP 4.3.2 or newer
*
* @package        CodeIgniter
* @author        Rick Ellis
* @copyright    Copyright (c) 2006, pMachine, Inc.
* @license        http://www.codeignitor.com/user_guide/license.html
* @link        http://www.codeigniter.com
* @since        Version 1.0
* @filesource
*/

// ------------------------------------------------------------------------

/**
* Pagination Class
*
* @package        CodeIgniter
* @subpackage    Libraries
* @category    Pagination
* @author        Rick Ellis
* @link        http://www.codeigniter.com/user_guide/libraries/pagination.html
*/
class CI_Pagination {

    var $base_url           = ''; // The page we are linking to
    var $total_rows         = ''; // Total number of items (database results)
    var $per_page           = 10; // Max number of items you want shown per page
    var $num_links          =  2; // Number of "digit" links to show before/after the currently viewed page
    var $cur_page           =  0; // The current page being viewed
    var $first_link           = '‹ First';
    var $next_link            = '>';
    var $prev_link            = '<';
    var $last_link            = 'Last ›';
    var $uri_segment        = 3;
    var $full_tag_open        = '';
    var $full_tag_close        = '';
    var $first_tag_open        = '';
    var $first_tag_close    = ' ';
    var $last_tag_open        = ' ';
    var $last_tag_close        = '';
    var $cur_tag_open        = '&nbsp;<b>';
    var $cur_tag_close        = '</b>';
    var $next_tag_open        = '&nbsp;';
    var $next_tag_close        = '&nbsp;';
    var $prev_tag_open        = '&nbsp;';
    var $prev_tag_close        = '';
    var $num_tag_open        = '&nbsp;';
    var $num_tag_close        = '';

    /**
     * Constructor
     *
     * @access    public
     * @param    array    initialization parameters
     */
    function CI_Pagination($params = array())
    {
        if (count($params) > 0)
        {
            $this->initialize($params);        
        }
        
        log_message('debug', "Pagination Class Initialized");
    }
    
    // --------------------------------------------------------------------
    
    /**
     * Initialize Preferences
     *
     * @access    public
     * @param    array    initialization parameters
     * @return    void
     */
    function initialize($params = array())
    {
        if (count($params) > 0)
        {
            foreach ($params as $key => $val)
            {
                if (isset($this->$key))
                {
                    $this->$key = $val;
                }
            }        
        }
    }
    
    // --------------------------------------------------------------------
    
    /**
     * Generate the pagination links
     *
     * @access    public
     * @return    string
     */    
    function create_links()
    {
        // If our item count or per-page total is zero there is no need to continue.
        if ($this->total_rows == 0 OR $this->per_page == 0)
        {
           return '';
        }

        // Calculate the total number of pages
        $num_pages = ceil($this->total_rows / $this->per_page);

        // Is there only one page? Hm... nothing more to do here then.
        if ($num_pages == 1)
        {
            return '';
        }

        // Determine the current page number.        
        $CI =& get_instance();    
        if ($CI->uri->segment($this->uri_segment) != 0)
        {
            $this->cur_page = $CI->uri->segment($this->uri_segment);
            
            // Prep the current page - no funny business!
            $this->cur_page = preg_replace("/[a-z\-]/", "", $this->cur_page);
        }
        else
        {
            $this->cur_page = 1;
        }
        
                
        if ( ! is_numeric($this->cur_page))
        {
            $this->cur_page = 0;
        }
        
        // Is the page number beyond the result range?
        // If so we show the last page
        if ($this->cur_page > $num_pages)
        {
            $this->cur_page = $num_pages;
        }
        
        $uri_page_number = $this->cur_page;
        //$this->cur_page = floor(($this->cur_page/$this->per_page) + 1);


        // Add a trailing slash to the base URL if needed
        $this->base_url = preg_replace("/(.+?)\/*$/", "\\1/",  $this->base_url);
        
          // And here we go...
        $pagination = '';
        
        // Render the "First" link
        if ($this->cur_page > 1)
        {
            $pagination .= $this->first_tag_open.'<a >base_url.'">'.$this->first_link.'</a>'.$this->first_tag_close;
        }
        else
            $pagination.= "<span class=\"disabled\">".$this->first_link."</span>";
        
        // Render the "previous" link
        if ($this->cur_page > 1)
        {
            $prev = $this->cur_page - 1;
            $pagination .= $this->prev_tag_open.'<a >base_url.$prev.'">'.$this->prev_link.'</a>'.$this->prev_tag_close;
        }
        else
            $pagination.= "<span class=\"disabled\">&lt;</span>";
        
        
        if ($num_pages < 7 + ($this->num_links * 2))    //not enough pages to bother breaking it up
        {
            for ($counter = 1; $counter <= $num_pages; $counter++)
            {
                if ($counter == $this->cur_page)
                {
                    $pagination .= "<span class=\"current\">$counter</span>"; // Current page
                }
                else
                {
                    $pagination .= $this->num_tag_open.'<a >base_url.$counter.'">'.$counter.'</a>'.$this->num_tag_close;
                }
            }
        }
        elseif($num_pages > 5 + ($this->num_links * 2))    //enough pages to hide some
        {
            //close to beginning; only hide later pages
            if($this->cur_page < 1 + ($this->num_links * 2))
            {
                for ($counter = 1; $counter < 4 + ($this->num_links * 2); $counter++)
                {
                    if ($counter == $this->cur_page)
                    {
                        $pagination .= "<span class=\"current\">$counter</span>"; // Current page
                    }
                    else
                    {
                        $pagination .= $this->num_tag_open.'<a >base_url.$counter.'">'.$counter.'</a>'.$this->num_tag_close;
                    }
                }
                
                $pagination.= "...";
                
                $num_pages_minus = $num_pages-1;
                $pagination .= $this->num_tag_open.'<a >base_url.$num_pages_minus.'">'.$num_pages_minus.'</a>'.$this->num_tag_close;
                $pagination .= $this->num_tag_open.'<a >base_url.$num_pages.'">'.$num_pages.'</a>'.$this->num_tag_close;    
            }
            //in middle; hide some front and some back
            elseif($num_pages - ($this->num_links * 2) > $this->cur_page && $this->cur_page > ($this->num_links * 2))
            {
                $one=1;
                $two=2;
        
                $pagination .= $this->num_tag_open.'<a >base_url.$one.'">'.$one.'</a>'.$this->num_tag_close;
                $pagination .= $this->num_tag_open.'<a >base_url.$two.'">'.$two.'</a>'.$this->num_tag_close;
                
                $pagination.= "...";
                
                for ($counter = $this->cur_page - $this->num_links; $counter <= $this->cur_page + $this->num_links; $counter++)
                {
                    if ($counter == $this->cur_page)
                    {
                        $pagination.= "<span class=\"current\">$counter</span>";
                    }
                    else
                    {
                        $pagination .= $this->num_tag_open.'<a >base_url.$counter.'">'.$counter.'</a>'.$this->num_tag_close;
                    }
                }
                $pagination.= "...";
        
                $num_pages_minus = $num_pages-1;
                $pagination .= $this->num_tag_open.'<a >base_url.$num_pages_minus.'">'.$num_pages_minus.'</a>'.$this->num_tag_close;
                $pagination .= $this->num_tag_open.'<a >base_url.$num_pages.'">'.$num_pages.'</a>'.$this->num_tag_close;    
            }
            //close to end; only hide early pages
            else
            {
                $one=1;
                $two=2;
        
                $pagination .= $this->num_tag_open.'<a >base_url.$one.'">'.$one.'</a>'.$this->num_tag_close;
                $pagination .= $this->num_tag_open.'<a >base_url.$two.'">'.$two.'</a>'.$this->num_tag_close;
                
                $pagination.= "...";
                
                for ($counter = $num_pages - (2 + ($this->num_links * 2)); $counter <= $num_pages; $counter++)
                {
                    if ($counter == $this->cur_page)
                    {
                        $pagination.= "<span class=\"current\">$counter</span>";
                    }
                    else
                    {
                        $pagination .= $this->num_tag_open.'<a >base_url.$counter.'">'.$counter.'</a>'.$this->num_tag_close;
                    }
                }
            }
        }
        
        // Render the "next" link
        if ($this->cur_page < $counter - 1)
        {
            $next = $this->cur_page + 1;
            $pagination .= $this->next_tag_open.'<a >base_url.$next.'">'.$this->next_link.'</a>'.$this->next_tag_close;
        }
        else
            $pagination.= "<span class=\"disabled\">&gt;</span>";
        
        
        // Render the "Last" link
        if ($this->cur_page < $counter - 1)
        {
            $pagination .= $this->last_tag_open.'<a >base_url.$num_pages.'">'.$this->last_link.'</a>'.$this->last_tag_close;
         }
        else
            $pagination.= "<span class=\"disabled\">".$this->last_link."</span>";

        // Kill double slashes.  Note: Sometimes we can end up with a double slash
        // in the penultimate link so we'll kill all double slashes.
        $pagination = preg_replace("#([^:])//+#", "\\1/", $pagination);

        // Add the wrapper HTML if exists
        $pagination = $this->full_tag_open.$pagination.$this->full_tag_close;
        
        return $pagination;        
    }
}
// END Pagination Class

/* End of file Pagination.php */
/* Location: ./system/libraries/Pagination.php */

Secondly, a new pagination plugin to keep our controller files a bit cleaner So, make a new file here /system/plugins/pagination_pi.php

&lt;?php

/**
 * Initalises pagination config settings and returns the create_links string.
 *
 * @param unknown_type $cur_page_seg
 * @param unknown_type $total_rows
 * @param unknown_type $per_page_val
 * @param unknown_type $per_page_seg
 * @param unknown_type $pbase_url
 * @return string
 */
function init_paginate ($cur_page_seg, $total_rows, $per_page_val, $per_page_seg, $pbase_url) 
{    
    $obj =& get_instance();
    
    //load relevant libraries
    $obj->load->library('pagination');
    
    //pagination setup
    //echo $per_page_val;
    $pbase_url = $pbase_url."/".$per_page_val;
    $config['base_url'] = $pbase_url;
    $config['total_rows'] = $total_rows;
    $config['per_page'] = $per_page_val; 
    $config['uri_segment'] = $cur_page_seg;
    
    //pagination initialization
    $obj->pagination->initialize($config); 
    
    return $obj->pagination->create_links();
    // end paging
}


/**
 * Works out how many records should be shown per page. The value will either be taken from
 *     1. The uri segment
 *  2. The default value set in the controller; or
 *  3. A default upper/lower range if people try to do tricky things in the uri segment (e.g. negative
 *     values).
 *
 * @param unknown_type $per_page_val
 * @param unknown_type $per_page_seg
 * @return unknown
 */
function get_per_page($per_page_val, $per_page_seg)
{    
    $per_page = validate_per_page($per_page_val, $per_page_seg);
    
    return $per_page;
}

/**
 * Validates the per page val and returns the value. Will check for out of bounds and non-numerical values
 *
 * @param unknown_type $per_page_val
 * @param unknown_type $per_page_seg
 * @return unknown
 */
function validate_per_page($per_page_val, $per_page_seg)
{
    $obj =& get_instance();
    
     //use per page if set from the uri, otherwise use default value set in controller
    $per_page = $obj->uri->segment($per_page_seg, $per_page_val);
    
    //security check - ensure value is numeric
    if (is_numeric($per_page)) : $per_page=$per_page; else: $per_page = $per_page_val; endif;
    
    //limit the per page value - prevents someone from doing a query of 1000 rows etc
    if ($per_page > 100) :    $per_page = 100; endif;
    
    //limit the per page value from negative numbers
    if ($per_page < 1) : $per_page = 10; endif;
    
    return $per_page;
}

/**
 * Will work out what offset should be used for a given sql query for a given pag
 *
 * @param unknown_type $cur_page_seg
 * @param unknown_type $per_page_val
 * @return unknown
 */
function get_offset($cur_page_seg, $per_page_val)
{
    $obj =& get_instance();
    
    //determine offset - per_page_val has already been validated
    $page = 1;
    if ($obj->uri->segment($cur_page_seg, 0)) : $page = $obj->uri->segment($cur_page_seg, 0); endif;
    if ( $page == 1):    $offset = 0; else :    $offset = ($page - 1) * $per_page_val; endif;
     
     return $offset;
}
?&gt;

Thirdly, our controller file. To use the pagination, add this to the controller.

        $this->load->model('My_Model', 'sql', TRUE);
        
        //********************SET UP PAGINATION VALUES****************************
        //set up per_page_value, per_page_seg, cur_page_seg and $data['pbase_url']
        //************************************************************************
        $this->load->plugin('pagination'); 
        
        $per_page_value = 5;  //default - unless overridden later
        $per_page_seg = 5;    //the uri segment for the per page value
        $cur_page_seg = 6;      //the url segment for the current page value (generally +1 of per page seg)
        
        $per_page = get_per_page($per_page_value, $per_page_seg); 
        $offset = get_offset($cur_page_seg, $per_page); 
        
        //generate the query
        $data['query'] = $this->sql->get_records($offset, $per_page);
        
        //find out the total amount of records
        $total_rows = count($this->sql->get_records(NULL, NULL));
        
        $data['pbase_url'] = base_url().'path/to/controller/';
        $data['pagination'] = init_paginate($cur_page_seg, $total_rows, $per_page, $per_page_seg, $data['pbase_url']); 
                
        $this->load->view('path/to/controller', $data);

Fourthly, an example function from out model file.

    function get_records($offset, $limit)
    {
               //add where clause processing here if required
        
        $this->db->select('*');    
        //$this->db->having($having);
        
                if ($offset != NULL)
            $this->db->offset($offset);
        if ($limit != NULL)
            $this->db->limit($limit);
        
        if ($limit == NULL && $offset == NULL)
        {
            $count = $this->db->get('my_table');
            return $count->result_array();
        }
        else
            return $this->db->get('my_table');
    }

Fifthly, our view file. Wherever you want the pagination to be, add this:

<div class="pagination" align="left">
    &lt;?php echo $pagination;?&gt;    
</div>
&lt;!-- this is the number of records part - an example only --&gt;
<div align="right"><a href="&lt;?=$pbase_url;?&gt;/10">10</a> | <a href="&lt;?=$pbase_url;?&gt;/20">20</a></div>

Sixthly, and also lastly, here is an example css file

div.pagination 
{
    padding: 3px;
    margin: 3px;
}

div.pagination a 
{
    padding: 2px 5px 2px 5px;
    margin: 2px;
    border: 1px solid #AAAADD;
    text-decoration: none; /* no underline */
    color: #000099;
}

div.pagination a:hover, div.pagination a:active 
{
    border: 1px solid #000099;
    color: #000;
}

div.pagination span.current 
{
    padding: 2px 5px 2px 5px;
    margin: 2px;
    border: 1px solid #000099;
    font-weight: bold;
    background-color: #993300;
    color: #FFF;
}
div.pagination span.disabled 
{
    padding: 2px 5px 2px 5px;
    margin: 2px;
    border: 1px solid #EEE;
    color: #DDD;
}

Hope it helps someone out there!

Clone this wiki locally