skip to content

PHP: Advanced Pagination

 Tweet0 Shares0 Tweets

As soon as you connect to a database with PHP you will come across situations when you want to make accessible more content than can be displayed on a single page.

The solution, ubiquitous these days, is to split your data over a number of pages while displaying navigation links to let people navigate between pages.


Here we've created a dataset of just the numbers 1 through 150, and broken them up into blocks of 20 per page using just the code shown in the next section:

Page 1 | 2 | 3 ... 7 | 8 | Next »

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20.

As you scroll through the pages, we adjust the links to try and keep them compact. This is more important when you have tens of pages. Basically, we're only displaying links that are within two positions of either end, or of the currently selected page.

The data displayed here is just numbers, but in reality each number would represent a row of data from a query, a product, search result, photo, blog entry, etc.

Pagination PHP Source Code

First we need to prepare our data and some basic variables. In the demonstration above we use the following, but you can replace them with real data:

<?PHP $NUMPERPAGE = 20; // max. number of items to display per page $this_page = "/php/pagination/"; $data = range(1, 150); // data array to be paginated $num_results = count($data); ?>

Then we use the following code to build the navigation links:

<?PHP # Original PHP code by Chirp Internet: # Please acknowledge use of this code by including this header. if(!isset($_GET['page']) || !$page = intval($_GET['page'])) $page = 1; // extra variables to append to navigation links (optional) $linkextra = []; if(isset($_GET['var1']) && $var1 = $_GET['var1']) { // repeat as needed for each extra variable $linkextra[] = "var1=" . urlencode($var1); } $linkextra = implode("&amp;", $linkextra); if($linkextra) $linkextra .= "&amp;"; // build array containing links to all pages $tmp = []; for($p=1, $i=0; $i < $num_results; $p++, $i += $NUMPERPAGE) { if($page == $p) { // current page shown as bold, no link $tmp[] = "<b>{$p}</b>"; } else { $tmp[] = "<a href=\"{$this_page}?{$linkextra}page={$p}\">{$p}</a>"; } } // thin out the links (optional) for($i = count($tmp) - 3; $i > 1; $i--) { if(abs($page - $i - 1) > 2) { unset($tmp[$i]); } } // display page navigation iff data covers more than one page if(count($tmp) > 1) { echo "<p>"; if($page > 1) { // display 'Prev' link echo "<a href=\"{$this_page}?{$linkextra}page=" . ($page - 1) . "\">&laquo; Prev</a> | "; } else { echo "Page "; } $lastlink = 0; foreach($tmp as $i => $link) { if($i > $lastlink + 1) { echo " ... "; // where one or more links have been omitted } elseif($i) { echo " | "; } echo $link; $lastlink = $i; } if($page <= $lastlink) { // display 'Next' link echo " | <a href=\"{$this_page}?{$linkextra}page=" . ($page + 1) . "\">Next &raquo;</a>"; } echo "</p>\n\n"; } ?>

expand code box

Displaying paginated data

We can display a single page - the currently selected page - of data by making use of the LimitIterator class:

<?PHP $data = new \ArrayIterator($data); // NOT needed if data is already an Iterator! $it = new \LimitIterator($data, ($page - 1) * $NUMPERPAGE, $NUMPERPAGE); try { $it->rewind(); foreach($it as $row) { echo $row; // display record } } catch(\OutOfBoundsException $e) { echo "Error: Caught OutOfBoundsException"; } ?>

The try/catch statement prevents the loop from running if the initial value provided is out of bounds. If $page is negative, for example, or too large for the dataset. If you like you can use the exception block to set the $page variable back to 1 and then run the loop.

Join the three code blocks together and you should have a working pagination system. Note that there are a few sections where you might want to add or remove some lines - as noted in the comments.

If you're not familiar with iterators in PHP, you might find this article enlightening.

A caveat to this approach is that if your query is expensive, and not cached, then instead of LimitIterator you are better off using a query with OFFSET and LIMIT to select just the rows to be displayed, and a separate COUNT query to determine the total number of results.

Including extra link variables

The 'extra variables' section lets you define extra values to be passed from page to page when the navigation links are used. This could be a search keyword, filter setting, user id, or something else.

Variables need to be encoded using urlencode and will be passed as GET variables - as is the page variable. Here's a more fleshed out example:

<PHP ... $linkextra = []; if(isset($_GET['search']) && $search = $_GET['search']) { $linkextra[] = "search=" . urlencode($search); } if(isset($_GET['color']) && $color = $_GET['color']) { $linkextra[] = "color=" . urlencode($color); } $linkextra = implode("&amp;", $linkextra); if($linkextra) $linkextra .= "&amp;"; ... ?>

This will result in a string "search=search&amp;color=color" being appended to all the navigation links, so the values are preserved betwen pages.

Are you using this code, do you have questions or comment, use the Feedback form below to get in touch.



Send a message to The Art of Web:

used only for us to reply, and to display your gravatar.


<- copy the digits from the image into this box

press <Esc> or click outside this box to close

Post your comment or question