Codex tools: Log in
Contents |
This page explains how to use offset in your queries without breaking WordPress's pagination.
Specifying hard-coded offsets in queries can and will break pagination since offset is used by WordPress internally to calculate and handle pagination. To fix this, you have to manually handle pagination by detecting whether there are extra pages and dynamically calculating the appropriate offset for the current page. You also need to adjust WordPress's pagination calculation to exclude your offset (ignored) posts, which can be done by using the found_posts filter and subtracting your offset.
This article assumes that you are already very familiar with WordPress's Plugin API. You should also be familiar with WordPress's $wpdb class object and it's various methods.
Offsets are useful because they can allow a developer to skip a certain number of WordPress posts before starting output.
Unfortunately, many developers find out that hard way that setting an offset value in their custom WordPress queries has a nasty and potentially serious habit of breaking pagination.
There is a very good reason for this however... the offset argument is actually the value WordPress uses to handle pagination itself. If a developer sets a manual offset value, pagination will not function because that value will override WordPress's automatic adjustment of the offset for a given page.
In order to use an offset in WordPress queries without losing WordPress's pagination features, you will need to manually handle some basic pagination calculations. This can accomplished with the following two hooks:
The first step is to use the pre_get_posts hook to both add your offset, and handle pagination. This hook passes the $query object to your function by reference, allowing you to easily use and manipulate the object without returning anything.
To make sure we are modifying the correct query, we perform a little check at the top. In the case of this example, we want the site's main blog archive (aka 'home') to skip the first 10 posts; we do this by checking any properties of the query object that may be relevant to us.
add_action('pre_get_posts', 'myprefix_query_offset', 1 );
function myprefix_query_offset(&$query) {
//Before anything else, make sure this is the right query...
if ( ! $query->is_posts_page ) {
return;
}
//First, define your desired offset...
$offset = 10;
//Next, determine how many posts per page you want (we'll use WordPress's settings)
$ppp = get_option('posts_per_page');
//Next, detect and handle pagination...
if ( $query->is_paged ) {
//Manually determine page query offset (offset + current page (minus one) x posts per page)
$page_offset = $offset + ( ($query->query_vars['paged']-1) * $ppp );
//Apply adjust page offset
$query->set('offset', $page_offset );
}
else {
//This is the first page. Just use the offset...
$query->set('offset',$offset);
}
}
There is one more problem to address, however. WordPress will *not* take the offset into account when it checks for the number of posts.
This creates a problem. Internally, WordPress will run a simple check to see if the number of pages returned by the query outnumbers the queries posts_per_page property. However, although offset will skip posts in the loop, those skipped posts will still counted.
The effect of this little miscalculation is that WordPress will sometimes assume there is another page even if another page does not exist. For example, if you have 15 posts, an offset of 10, and a posts_per_page limit of 25, WordPress will assume that there is a second page even though the first page will only contain 5 posts (which falls short of our 25 posts_per_page limit). That mysterious second page will return a 404 error.
This pagination problem is easily corrected by using the found_posts filter hook, like so...
add_filter('found_posts', 'myprefix_adjust_offset_pagination', 1, 2 );
function myprefix_adjust_offset_pagination($found_posts, $query) {
//Define our offset again...
$offset = 10;
//Ensure we're modifying the right query object...
if ( $query->is_posts_page ) {
//Reduce WordPress's found_posts count by the offset...
return $found_posts - $offset;
}
}
And now you're done. Pagination should now work correctly along with your own, custom offset.