WordPress Custom Loop Pagination

Using WordPress normal loop to achieve something completely different is often not possible, So two things comes first. Either to use query_posts or wp_query. But its always better to use wp_query. There is almost nothing you cannot achieve using wp_query. But whenever Pagination comes in wp_query, its all messed up and even WordPress Codex do not provide sufficient documentation. Recently I faced a similar problem while trying to show pagination in custom loop. But after having some hard time I came up with a solution that I would like to share here.

 

The reason why Pagination works correctly in normal loop but not in custom loop is that WordPress pagination works only for $wp_query global variable . And this is what normal loop access. In normal loop have_posts calls $wp_query->have_posts and the_post calls $wp_query->the_post.

So an easy way to make Pagination work in custom loop is to make WordPress think that we are using $wp_query. But messing with $wp_query is not always a good idea. But still we can make a safe use of it. Here the code how I achieved the pagination work.

<?php
/**
 * Displays the Pagination in Custom loop
 *
 */
?>
<?php get_header(); ?>
$temp = $wp_query; //save old query
$wp_query= null; //clear $wp_query

//The query
$wp_query = new WP_Query();
$wp_query->query( array( 'post_type' =>'custom', 'posts_per_page'=>5, 'paged' => $paged ) );
//The loop
while ($wp_query->have_posts()): $wp_query->the_post(); 

     //loop part

endwhile;

//Pagination part
if(function_exists('wp_pagenavi') ) {
     wp_pagenavi(); //function call for plugin pagination( wp pagenavi plugin)
}else{ ?>
     <ul class="pagination">
          <li class="previous"><?php next_posts_link('Previous'); ?></li>
	  <li class="next"><?php previous_posts_link('Next'); ?></li>
     </ul>
<?php
}//endif
wp_reset_postdata();
$wp_query = null; //Reset the normal query
$wp_query = $temp;//Restore the query

get_footer(); ?>

After Following the above code my pagination worked but there was still a problem left. And the issue was more bigger than I thought. My pagination was working fine but when i visited my last page it was throwing 404 error. And here was how this issue was being created.

This 404 page issue was only occurring for last page of my pagination. Actually if you set your posts per page value in your custom loop inside custom template less than the one that has been set in admin back-end then the last page was throwing 404 problem. But when I set my post per page value greater in my custom loop than the one set in my backend there was no problem and my pagination was working fine. This was happening because post per page value set in custom loop was lost when switching from one page to another while pagination.
Here is an example for more clear view:
suppose we have 11 posts in our custom post type
First, set custom loop post per page value greater than the one set in backend:
custom loop post per page = 8
backend post per page = 5

so when template gets loaded and custom loop runs it shows 8 posts in page 1 when next page is clicked the post per page value set in custom loop gets lost and thinks the post per page value is 5 so it calculates that it is in page 2 and it has to show posts from 6 -10 but as it executes the loop it is again reminded by post per page value inside the loop to be 8 so instead of showing 6-10 it shows posts from 9-11 and our pagination seems to work fine.

Now lets set our post per page value smaller than the one set in admin backend:
Total Posts = 11
custom loop post per page = 5
backend post per page = 10
Now on page one the template custom loop shows 5 posts 1-5 when next page is clicked the post per page value set in custom loop gets lost and it thinks the post per page value is 10 i.e set in backend so in page 2 it calculates it has to show only one post in page 2 i.e page 2 exists then again the loop is executed and it is reminded that post per page value is 5 and it is in page 2 so it shows posts 6-10 now again when next page is clicked the post per page value in loop is again lost and it think it is 10, now it calculates it is in page 3 and as total posts is 11 so this page 3 don’t exists and it loads 404 page. But in real there is still one post to be shown according to the post per page set inside the loop and page 3 must exist.

The easy solution to this is to set the post per page value in your custom template loop to be greater than the post per page value in backend but this is also not always efficient. But at least we can carefully give post per page value in backend.
Alternatively here is another solution i.e. using WordPress hook to set post per page value. But remember you make your post per page value same in your custom loop and inside the function hook below. The function uses request filter which executes before the main query.

<?php
/**
 * Alter the post per page
 * @uses request filter
 * executes before main query is executed and database is accessed
 */
function my_post_per_page( $query_string ){
     if ( !is_admin() ){
          if ( isset( $query_string['post_type'] ) ){//case post type
               $query_string['post_type']=='custom_post_type_name'
                $query_string['posts_per_page'] = 5 ;
           }
           elseif( isset( $query_string['category_name'] ) ) {//can apply to any category
                $query_string['category_name']== $category;
                $query_string['posts_per_page'] = 6 ;
           }
           elseif( isset( $query_string['s'] ) ) { search case
               $query_string['posts_per_page'] = 7;
           }
     }
}
add_filter('request', 'my_post_per_page');

Hence by finding out appropriate $query_string you can change post per page value of any desired template file. But if you want to just slightly alter the normal loop then using pre_get_posts hook is the best way.This hook fires before the main query is run. I hope this post solves the problem of pagination on custom loop. If you have any queries feel free to comment. I will try my best to answer you back.

WordPress Custom Loop Pagination