I've been working on a new code base for StreetWise these past couple months, getting ready for the upcoming busy season, and as this new code needs to support membership of 1,000,000+ people decided I wanted, no, needed, to rework my pagination algorithm.
The Problem
Previously, I've gone for a basic approach of just listing out the links sequentially with a Next and Prev state. For larger page sets, I would just remove the numeric links. For example they would look like the below:
1 2 3 4 5 etc...
This becomes problematic at higher numbers though; just imagine the page would look if there were 1,000 pages instead of five. Here's an example of just 100:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
So it can be a bit of a problem.
The Replacement
A lot of websites are using a better pagination style that's sometimes called Wordpress pagination. This pagination style presents only a few links at a time while still allowing more freedom than the basic Next Prev style. Here's an example:
« Previous 1 2 3 ... 10 11 12 13 14 ... 118 119 120 Next »
The Solution
Being it's called Wordpress pagination, and that I'm a lazy php programmer, I just stole the code from Wordpress and translated it into something a little more portable.
function paginate_links($format = '?page=%#%', $total = '1', $current = '0', $base = '%_%', $show_all = FALSE, $prev_next = TRUE, $prev_text = '« Previous', $next_text = 'Next »',$end_size = '3',$mid_size = '2',$type = 'plain') {
$total = (int) $total;
if($total < 2) {
return;
}
$current = (int) $current;
$end_size = 0 < (int) $end_size ? (int) $end_size : 1; // Out of bounds? Make it the default.
$mid_size = 0 <= (int) $mid_size ? (int) $mid_size : 2;
$r = '';
$page_links = array();
$n = 0;
$dots = false;
if ( $prev_next && $current && 1 < $current ){
$link = str_replace('%_%', 2 == $current ? '' : $format, $base);
$link = str_replace('%#%', $current - 1, $link);
$link .= $add_fragment;
$page_links = "$n_display";
$dots = true;
} else {
if ( $show_all || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) {
$link = str_replace('%_%', 1 == $n ? $format : $format, $base);
$link = str_replace('%#%', $n, $link);
$link .= $add_fragment;
$page_links = "...";
$dots = false;
}
}
}
if ( $prev_next && $current && ( $current < $total || -1 == $total ) ) {
$link = str_replace('%_%', $format, $base);
$link = str_replace('%#%', $current + 1, $link);
$link .= $add_fragment;
$page_links[] = "$next_text";
}
switch ( $type ) {
case 'array' :
return $page_links;
break;
case 'list' :
$r .= "
- \n\t
- "; $r .= join(" \n\t
- ", $page_links); $r .= " \n
As you can see, especially if you're already familiar with Wordpress, all I've done is take out the Wordpress specific functions and set the $args to function parameters. Pure theft 😊
There are a bunch of paramaters and options you can configure the links by but only the first 3 are really needed to make the function work.
Here's an example:
$numofpages = 20;
$page = '4'
$query_string = 'foo='.$foo.'&bar='.$bar;
echo paginate_links('?'.$query_string.'&page=%#%', $numofpages, $page);
I didn't put too much work into this, which is kinda the point, but I hope it's helpful!