TailTemplate Build stunning websites faster with our pre-designed Tailwind CSS templates

PHP pagination tutorial

Pagination is a very common task for a PHP developer. With the power of OOP coding style, it is nature to have a reusable pagination class, which we can use it for various projects. Because for an advanced developer, we should never repeat the same coding process.(Don't Repeat Yourself). In this tutorial, I will go through the process of writing your very own PHP paginator class.

Introduction

During a normal PHP coding task, pagination is always associated with a database (MySql in most cases). However in this tutorial, we are going to separate pagination from data source in order to achieve the goal of a reusable pagination class. So you do not have to worry about the data source (database) part. All of our concerns will be the pagination numbers as well as navigation.

Firstly, let me show you the completed class, and then I will explain it in details:

class Paginator {      
        public $itemsPerPage;
        public $range;
        public $currentPage;
        public $total;
        public $textNav;
        public $itemSelect;
        private $_navigation;      
        private $_link;
        private $_pageNumHtml;
        private $_itemHtml;
        /**
         * Constructor
         */
        public function __construct()
        {
            //set default values
            $this->itemsPerPage = 5;
            $this->range        = 5;
            $this->currentPage  = 1;       
            $this->total        = 0;
            $this->textNav      = false;
            $this->itemSelect   = array(5,25,50,100,'All');        
            //private values
            $this->_navigation  = array(
                    'next'=>'Next',
                    'pre' =>'Pre',
                    'ipp' =>'Item per page'
            );         
            $this->_link         = filter_var($_SERVER['PHP_SELF'], FILTER_SANITIZE_STRING);
            $this->_pageNumHtml  = '';
            $this->_itemHtml     = '';
        }
         
        /**
         * paginate main function
         *
         * @author              The-Di-Lab <thedilab@gmail.com>
         * @access              public
         * @return              type
         */
        public function paginate()
        {
            //get current page
            if(isset($_GET['current'])){
                $this->currentPage  = $_GET['current'];    
            }          
            //get item per page
            if(isset($_GET['item'])){
                $this->itemsPerPage = $_GET['item'];
            }          
            //get page numbers
            $this->_pageNumHtml = $this->_getPageNumbers();        
            //get item per page select box
            $this->_itemHtml    = $this->_getItemSelect(); 
        }
                 
        /**
         * return pagination numbers in a format of UL list
         *
         * @author              The-Di-Lab <thedilab@gmail.com>
         * @access              public
         * @param               type $parameter
         * @return              string
         */
        public function pageNumbers()
        {
            if(empty($this->_pageNumHtml)){
                exit('Please call function paginate() first.');
            }
            return $this->_pageNumHtml;
        }
         
        /**
         * return jump menu in a format of select box
         *
         * @author              The-Di-Lab <thedilab@gmail.com>
         * @access              public
         * @return              string
         */
        public function itemsPerPage()
        {         
            if(empty($this->_itemHtml)){
                exit('Please call function paginate() first.');
            }
            return $this->_itemHtml;   
        }
         
        /**
         * return page numbers html formats
         *
         * @author              The-Di-Lab <thedilab@gmail.com>
         * @access              public
         * @return              string
         */
        private function  _getPageNumbers()
        {
            $html  = '<ul>';
            //previous link button
            if($this->textNav&&($this->currentPage>1)){
                echo '<li><a href="'.$this->_link .'?current='.($this->currentPage-1).'"';
                echo '>'.$this->_navigation['pre'].'</a></li>';
            }          
            //do ranged pagination only when total pages is greater than the range
            if($this->total > $this->range){               
                $start = ($this->currentPage <= $this->range)?1:($this->currentPage - $this->range);
                $end   = ($this->total - $this->currentPage >= $this->range)?($this->currentPage+$this->range): $this->total;
            }else{
                $start = 1;
                $end   = $this->total;
            }   
            //loop through page numbers
            for($i = $start; $i <= $end; $i++){
                    echo '<li><a href="'.$this->_link .'?current='.$i.'"';
                    if($i==$this->currentPage) echo "class='current'";
                    echo '>'.$i.'</a></li>';
            }          
            //next link button
            if($this->textNav&&($this->currentPage<$this->total)){
                echo '<li><a href="'.$this->_link .'?current='.($this->currentPage+1).'"';
                echo '>'.$this->_navigation['next'].'</a></li>';
            }
            $html .= '</ul>';
            return $html;
        }
         
        /**
         * return item select box
         *
         * @author              The-Di-Lab <thedilab@gmail.com>
         * @access              public
         * @return              string
         */
        private function  _getItemSelect()
        {
            $items = '';
            $ippArray = $this->itemSelect;             
            foreach($ippArray as $ippOpt){  
                $items .= ($ippOpt == $this->itemsPerPage) ? "<option selected value=\"$ippOpt\">$ippOpt</option>\n":"<option value=\"$ippOpt\">$ippOpt</option>\n";
            }              
            return "<span class=\"paginate\">".$this->_navigation['ipp']."</span>
            <select class=\"paginate\" onchange=\"window.location='$this->_link?current=1&item='+this[this.selectedIndex].value;return false\">$items</select>\n";     
        }
}

Code Explaination

The class name is "Paginator". Firstly let us take a look at its member variables:

public $itemsPerPage;
public $range;
public $currentPage;
public $total;
public $textNav;
private $_navigation;      
private $_link;
private $_pageNumHtml;
private $_itemHtml;
  • public $itemsPerPage: This tells Paginator class how many items per page.
  • public $range:: This variable set the range between current page and two ending page numbers. For example, if current page is 5, and range is 2, pagination numbers will be showed as 3,4,5,6,7. Current page will be the middle page number.
  • **public $currentPage:**This variable stores current page number.
  • **public $total:**This variable stores total number of pages.
  • **public $textNav:**This variable decides if to show text navigation(Pre,Next) links. This approach makes it flexiable to hide/show the text navigation.
  • **public $itemSelect:**This variable is an array of numbers, which is used to form the items per page select box.
  • **private $_navigation:**This variable stores all the text strings include 'Pre','Next' as well as 'Item per pages'. This approach makes it easy to change the text.
  • **private $_link:**This variable stores the link value, which is the sanitized $_SERVER['PHP_SELF'] value.
  • **private $_pageNumHtml:**This variable stores the html content for paginations. We store it here to avoid repeated formatting process.
  • **private $_itemHtml:**This variable stores the html content for items per page select box. We store it here to avoid repeated formatting process.

All the variables are initialized in the class constructor. Take a look at it to have an idea of their default values:

public function __construct()
{
    //set default values
    $this->itemsPerPage = 5;
    $this->range        = 5;
    $this->currentPage  = 1;       
    $this->total        = 0;
    $this->textNav      = false;
    $this->itemSelect   = array(5,25,50,100,'All');        
    //private values
    $this->_navigation  = array(
            'next'=>'Next',
            'pre' =>'Pre',
            'ipp' =>'Item per page'
    );         
    $this->_link         = filter_var($_SERVER['PHP_SELF'], FILTER_SANITIZE_STRING);
    $this->_pageNumHtml  = '';
    $this->_itemHtml     = '';
}

First public function is paginate(), this function is the main function used to process the pagination:

public function paginate()
{
    //get current page
    if(isset($_GET['current'])){                        //1
        $this->currentPage  = $_GET['current'];    
    }          
    //get item per page
    if(isset($_GET['item'])){                           //2
        $this->itemsPerPage = $_GET['item'];
    }          
    //get page numbers
    $this->_pageNumHtml = $this->_getPageNumbers();     //3
    //get item per page select box
    $this->_itemHtml    = $this->_getItemSelect();      //4
}    
  1. We use $_GET['current'] to get the current page number from URL and set it to $currentPage if it is set.

  2. We use $_GET['item'] to get number of items per page from URL and set it to $itemsPerPage if it is set.

  3. We call a private function $_getPageNumbers() to process the pagination numbers and store it to variable $_pageNumHtml.

  4. We call a private function $_getItemSelect() to process the items per page select box and store it to variable $_itemHtml.


As we have seen from last function, two private functions ($_getPageNumbers() and $_getItemSelect()) are actually doing the job of generating the pagination html content. So let us take a look at these two functions:

private function  _getPageNumbers()
{
    $html  = '<ul>';
    //previous link button
    if($this->textNav&&($this->currentPage>1)){             //*********1
        echo '<li><a href="'.$this->_link .'?current='.($this->currentPage-1).'"';
        echo '>'.$this->_navigation['pre'].'</a></li>';
    }          
    //do ranged pagination only when total pages is greater than the range
    if($this->total > $this->range){                        //*********2
        $start = ($this->currentPage <= $this->range)?1:($this->currentPage - $this->range);
        $end   = ($this->total - $this->currentPage >= $this->range)?($this->currentPage+$this->range): $this->total;
    }else{
        $start = 1;
        $end   = $this->total;
    }   
    //loop through page numbers
    for($i = $start; $i <= $end; $i++){                     //*********3
            echo '<li><a href="'.$this->_link .'?current='.$i.'"';
            if($i==$this->currentPage) echo "class='current'";
            echo '>'.$i.'</a></li>';
    }          
    //next link button
    if($this->textNav&&($this->currentPage<$this->total)){  //*********4
        echo '<li><a href="'.$this->_link .'?current='.($this->currentPage+1).'"';
        echo '>'.$this->_navigation['next'].'</a></li>';
    }
    $html .= '</ul>';
    return $html;
}
  1. Set pre text link when $textNav is set to true and $currentPage is greater than 1.
  2. This process is to figure out the $start and $end paging numbers.
IF (total page > range) {
	start page = (current page <= range) ?  1 :  current page - range;
	end page   = (total page - current page >= range) ? current page +  range: total;
}ELSE{
	start page = 1;
	end page   = total page;
} 
  1. Loop using the $start and $end value to form the paging numbers as well as its links.
  2. Set next text link when $textNav is set to true and $currentPage is less than total page.
   private function  _getItemSelect()
   {
       $items = '';
       $ippArray = $this->itemSelect;             
       foreach($ippArray as $ippOpt){  
           $items .= ($ippOpt == $this->itemsPerPage) ? "<option selected value=\"$ippOpt\">$ippOpt</option>\n":"<option value=\"$ippOpt\">$ippOpt</option>\n";
       }              
       return "<span class=\"paginate\">".$this->_navigation['ipp']."</span>
       <select class=\"paginate\" onchange=\"window.location='$this->_link?current=1&item='+this[this.selectedIndex].value;return false\">$items</select>\n";     
   }      

This function is very straightforward. It simply uses a foreach loop to form a select box.


Lastly, let us take a look at two public functions, which will be actually called by developers to print out the pagination.

public function pageNumbers()
{
    if(empty($this->_pageNumHtml)){
        exit('Please call function paginate() first.');
    }
    return $this->_pageNumHtml;
}

As you can see, besides from returning the pagination html, this function always provides a debug message, reminding people to call paginate() function first, This will be discussed on How To Use section. The reason, we have a separate function to return the html content is that it allows developers to print out the pagination multiple times without reprocessing.

public function itemsPerPage() is almost identical to pageNumbers().

How To Use

To use this class, there are something you will need to do with your data source (MySql possibly, but can be anything). Firstly you will need to tell Paginator total number of records by setting variable $total. Secondly you will need to set your data record using combination of $currentPage and $itemsPerPage from Paginator class. For example, I am using MySql to store my data. What I need to do is firstly find total number of records (SELECT COUNT) using MySql and set it to $total:

//using mysql to find out total records
$totalRecords = using_mysql_find_out ("Select count(*) from table");
 
include 'paginator.php';
$paginator = new Paginator();
$paginator->total = $totalRecords;
$paginator->paginate();

After that, you will typically use "LIMIT" query to get data from database:

//using mysql to find out total records
$totalRecords = using_mysql_find_out ("Select count(*) from table");
 
include 'paginator.php';
$paginator = new Paginator();
$paginator->total = $totalRecords;
$paginator->paginate();
 
//get record from database and show
$records = using_mysql_find_out "Select (*) from table LIMIT ($paginator->currentPage-1)*$paginator->itemsPerPage,  $paginator->itemsPerPage" ;

Finally, you can call pageNumbers() function to print the pagination, and itemsPerPage() to print the items per page select box. Even more, with the way this class is built. You can print both of them multiple times, and it will not cause any performance issue:

//using mysql to find out total records
$totalRecords = using_mysql_find_out ("Select count(*) from table");
 
include 'paginator.php';
$paginator = new Paginator();
$paginator->total = $totalRecords;
$paginator->paginate();
 
//get record from database and show
$records = using_mysql_find_out "Select (*) from table LIMIT ($paginator->currentPage-1)*$paginator->itemsPerPage,  $paginator->itemsPerPage" ;
 
//print
echo $paginator->pageNumbers();
echo $paginator->itemsPerPage();

Since this class does not come with any style sheet, you should provide some basic style sheet for it to look better.

Download

You can download this script from my github account.

The End

Thank you for reading this article, and if you have any encountered anything different, have a different solution or think our solution is wrong, do let us know in the comment section. We will be very happy to hear that.

If you like our tutorial, please follow us on Twitter and help spread the word. We need your support to continue.