Some while ago, I built a pagination system in PHP to display a long list of pagination links. The pagination system showed at most 5 links at a time and additional links were represented by ellipsis (...). However, I was not able to deal with certain edge cases, so I drew some inspiration from the pagination system used on StackOverflow to tackle them. This is what the end result looked like:
- The first and last page links are always visible
- The previous and next page links (numeric ones) are always visible
- At most n page links excluding first and last page links are visible; rest are represented by ellipsis
- The pagination for first and last n - 1 pages is handled a bit differently
Complete PHP source code below.
Pagination Function
<?php /** * Displays pagination links based on given parameters * * @param int $currentPage - current page * @param int $itemCount - number of items to paginate, used to calculate total number of pages * @param int $itemsPerPage - number of items per page, used to calculate total number of pages * @param int $adjacentCount - half the number of page links displayed adjacent to the current page * @param (string|callable) $pageLinkTemplate - pagination URL string containing %d placeholder or a callable function that accepts page number and returns page URL * @param boolean $showPrevNext - whether to show previous and next page links * @return void */ function pagination($currentPage, $itemCount, $itemsPerPage, $adjacentCount, $pageLinkTemplate, $showPrevNext = true) { $firstPage = 1; $lastPage = ceil($itemCount / $itemsPerPage); if ($lastPage == 1) { return; } if ($currentPage <= $adjacentCount + $adjacentCount) { $firstAdjacentPage = $firstPage; $lastAdjacentPage = min($firstPage + $adjacentCount + $adjacentCount, $lastPage); } elseif ($currentPage > $lastPage - $adjacentCount - $adjacentCount) { $lastAdjacentPage = $lastPage; $firstAdjacentPage = $lastPage - $adjacentCount - $adjacentCount; } else { $firstAdjacentPage = $currentPage - $adjacentCount; $lastAdjacentPage = $currentPage + $adjacentCount; } echo '<div>'; if ($showPrevNext) { if ($currentPage == $firstPage) { echo '<span><</span>'; } else { echo '<a href="' . (is_callable($pageLinkTemplate) ? $pageLinkTemplate($currentPage - 1) : sprintf($pageLinkTemplate, $currentPage - 1)) . '"><</a>'; } } if ($firstAdjacentPage > $firstPage) { echo '<a href="' . (is_callable($pageLinkTemplate) ? $pageLinkTemplate($firstPage) : sprintf($pageLinkTemplate, $firstPage)) . '">' . $firstPage . '</a>'; if ($firstAdjacentPage > $firstPage + 1) { echo '<span>...</span>'; } } for ($i = $firstAdjacentPage; $i <= $lastAdjacentPage; $i++) { if ($currentPage == $i) { echo '<b>' . $i . '</b>'; } else { echo '<a href="' . (is_callable($pageLinkTemplate) ? $pageLinkTemplate($i) : sprintf($pageLinkTemplate, $i)) . '">' . $i . '</a>'; } } if ($lastAdjacentPage < $lastPage) { if ($lastAdjacentPage < $lastPage - 1) { echo '<span>...</span>'; } echo '<a href="' . (is_callable($pageLinkTemplate) ? $pageLinkTemplate($lastPage) : sprintf($pageLinkTemplate, $lastPage)) . '">' . $lastPage . '</a>'; } if ($showPrevNext) { if ($currentPage == $lastPage) { echo '<span>></span>'; } else { echo '<a href="' . (is_callable($pageLinkTemplate) ? $pageLinkTemplate($currentPage + 1) : sprintf($pageLinkTemplate, $currentPage + 1)) . '">></a>'; } } echo '</div>'; } ?>
Example 1
pagination(1, 123, 10, 2, "users.php?page=%d");
Example 2: Using canonical URL for first page
pagination(1, 123, 10, 2, function($page) { // use a callback function to handle special cases e.g. // return canonical URL for first page and page URL for rest return $page == 1 ? "/users/" : "/users/$page/"; });