This tutorial is intended for developers who wish to give their users the ability to step through a large number of database rows in manageable chunks instead of the whole lot in one go.
It is assumed that you already have basic knowledge of PHP and MySQL. The techniques described in this document are written with MySQL in mind but can be applied to any database that allows the LIMIT and OFFSET clauses on the SELECT statement.
If you have a form which allows the user to browse through the rows in a database table, what do you do if that table has hundreds or even thousands of rows? It would not be a good idea to show all those rows in a single form, instead you should split the database output into more manageable chunks or 'pages'. There are two things you must do:
Pagination Area
This area tells the user which page is currently being viewed, the total number of pages available, and contains links to go either forwards or backwards through the available pages. The hyperlinks are displayed as simple text if there are no previous or next pages. Note that these hyperlinks do not put the word FIRST, PREV, NEXT or LAST as a parameter in the URL string - they all specify an absolute page number in the format pageno=n
.
To include all the relevant functionality in a single script you should follow the steps outlined below.
This code will obtain the required page number from the $_GET array. Note that if it is not present it will default to 1.
if (isset($_GET['pageno'])) { $pageno = $_GET['pageno']; } else { $pageno = 1; } // if
This code will count how many rows will satisfy the current query.
$query = "SELECT count(*) FROM table WHERE ..."; $result = mysql_query($query, $db) or trigger_error("SQL", E_USER_ERROR); $query_data = mysql_fetch_row($result); $numrows = $query_data[0];
This code uses the values in $rows_per_page
and $numrows
in order to identify the number of the last page.
$rows_per_page = 15; $lastpage = ceil($numrows/$rows_per_page);
This code checks that the value of $pageno
is an integer between 1 and $lastpage
.
$pageno = (int)$pageno; if ($pageno > $lastpage) { $pageno = $lastpage; } // if if ($pageno < 1) { $pageno = 1; } // if
This code will construct the LIMIT clause for the sql SELECT statement.
$limit = 'LIMIT ' .($pageno - 1) * $rows_per_page .',' .$rows_per_page;
Now we can issue the database qery and process the result.
$query = "SELECT * FROM table $limit"; $result = mysql_query($query, $db) or trigger_error("SQL", E_USER_ERROR); ... process contents of $result ...
Finally we must construct the hyperlinks which will allow the user to select other pages. We will start with the links for any previous pages.
if ($pageno == 1) { echo " FIRST PREV "; } else { echo " <a href='{$_SERVER['PHP_SELF']}?pageno=1'>FIRST</a> "; $prevpage = $pageno-1; echo " <a href='{$_SERVER['PHP_SELF']}?pageno=$prevpage'>PREV</a> "; } // if
Next we inform the user of his current position in the sequence of available pages.
echo " ( Page $pageno of $lastpage ) ";
This code will provide the links for any following pages.
if ($pageno == $lastpage) { echo " NEXT LAST "; } else { $nextpage = $pageno+1; echo " <a href='{$_SERVER['PHP_SELF']}?pageno=$nextpage'>NEXT</a> "; echo " <a href='{$_SERVER['PHP_SELF']}?pageno=$lastpage'>LAST</a> "; } // if
That's all there is to it. Easy peasy, lemon squeezy.
Rather than have all my presentation, business and data access code in a single script I prefer to use the 3 tier architecture as described in my article A Development Infrastructure for PHP. As an added complication all my HTML output is generated from XSL stylesheets which use XML data files produced by my PHP scripts. The steps are exactly the same, but the code is slightly different.
If a page number is supplied in the $_GET array this code will pass it to the database object.
if (isset($_GET['page'])) { $dbobject->setPageNo($_GET['page']); } // if
The object uses this code to receive the passed value.
function setPageNo ($pageno) // this allows a particular page number to be selected { $this->pageno = abs((int)$pageno); } // setPageNo
As I serialise my object in the $_SESSION array if a new value is not supplied then I use whatever value was available in the previous instance.
This code is the same as before.
Here the result depends on the vaue held in $this->rows_per_page
. This is set to a default value in the class constructor, but it may be set to any other value at runtime.
if ($this->rows_per_page > 0) { $this->lastpage = ceil($this->numrows/$this->rows_per_page); } else { $this->lastpage = 1; } // if
This code is the same as before.
This code will construct the LIMIT clause for the sql SELECT statement. Note that if the value of $rows_per_page
has been set to zero then this effectively turns pagination off.
if ($rows_per_page > 0) { $limit = 'LIMIT ' .($pageno - 1) * $rows_per_page .',' .$rows_per_page; } else { $limit = ''; } // if
This code is the same as before.
This is a bit more complicated as it involves passing a series of parameters to an XSL stylesheet during the transformation process. Stage 1 is to extract certain values from the database object and insert them into any array.
$xsl_params['curpage'] = $dbobject->getPageNo(); $xsl_params['lastpage'] = $dbobject->getLastPage();
Stage 2 is to perform the XSL transformation using an XML file which was constructed by the PHP script and a predefined XSL stylesheet. These processes are described in detail in the following documents:
Stage 3 is performed during the XSL transformation. Although pagination is required in many scripts the code is absolutely identical, so being an efficient programmer I have defined it just once in its own template file. As this code has already been described in Generating dynamic web pages using XSL and XML I shall not bother to duplicate it here.
This code can be included in any number of XSL stylesheets using the followng line of code:
<xsl:include href="std.pagination.xsl"/>
The predefined template can be executed when required using the following line of code:
<xsl:call-template name="pagination" />
That's all there is to it. Easy peasy, lemon squeezy.
Sample software which shows pagination in action is available on my website, as described in article A Sample PHP Application. This software can be run online, or you can download all the source code and run it locally in your own PHP/MySQL environment.