Advertisement

Custom Post Type Pagination Chaining Method

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

Custom post type pagination got you down? There's been nothing more frustrating in developing for WordPress than getting custom post type pagination to work. I've developed a method that's solved my woes and I think it'll solve yours too.


Introduction

As I've started to create more premium WordPress themes I've begun developing a base theme as a sort of framework to build on with every new project. The process was going good until I started working with custom post types. That's when I inevitably went up against my long time foe, custom post type pagination.

Since the release of custom post types in WordPress 2.9 their pagination has proven difficult depending on the circumstance. Even heavy-hitting WordPress professionals have been stumped from time to time.

Thankfully, I feel I've finally beat custom post type pagination once and for all. I imagine myself flexing over it in the glow of the moonlight, my face chiseled in accomplishment, one foot planted on the ground and the other firmly on its chest.

My key to easy custom post type pagination is using the archive-posttype.php template to do something I call chaining, meaning that we use them as includes in other WordPress template files where pagination is needed. What this does is cut down on custom development for different circumstances in which a developer would want pagination. Think of it as using the custom post type archive template as an index or catch-all. There's a few twists along the way, but that's the big idea. Let's begin.


Step 1 Pagination

Since coding is a foundation science lets start with the issue of pagination itself. Even though I bow to the greatness of the pagination plugin, WP PageNavi, as Jacob Goldman recently pointed out, there's really no need for it. WordPress has its own pagination function called paginate_links() and apparently most developers know nothing about it. Be sure to read its documentation, but to save you the time of fashioning your own code for functions.php here's mine similar to the Codex example:

function paginate() {
	global $wp_query, $wp_rewrite;
	$wp_query->query_vars['paged'] > 1 ? $current = $wp_query->query_vars['paged'] : $current = 1;
	$pagination = array(
		'base' => @add_query_arg('page','%#%'),
		'format' => '',
		'total' => $wp_query->max_num_pages,
		'current' => $current,
		'show_all' => true,
		'type' => 'plain'
	);
	if ( $wp_rewrite->using_permalinks() ) $pagination['base'] = user_trailingslashit( trailingslashit( remove_query_arg( 's', get_pagenum_link( 1 ) ) ) . 'page/%#%/', 'paged' );
	if ( !empty($wp_query->query_vars['s']) ) $pagination['add_args'] = array( 's' => get_query_var( 's' ) );
	echo paginate_links( $pagination );
}

My chaining method has been developed with this function in mind. Using WP PageNavi for custom post type pagination gets real ugly so I won't be showing you how to do that for the same reason friends don't let friends drive drunk. You're welcome. But let's move on to what you really came for - finally figuring out how to keep custom post type pagination from throwing a 404 or always reverting back to page one.


Step 2 Custom Post Type Archive Template

Since custom post types don't have their own index pages, and like I said before, I think of the archive-posttype.php template as its stand-in specifically because I use it as the foundation for pagination in all other templates. A lot of developers will first stress a Page Template as an index page, but 1.) I obviously disagree and 2.) we'll get to those later. Even WordPress superstar, Justin Tadlock, agrees custom post types should have at least the option of their own index pages.

Thankfully, my paginate() function out of the box with the archive-posttype.php template. Phew. But the problem is its pagination is bound by the setting for posts per page in Settings > Reading. And because custom post types are just that, custom, nine times out of ten a developer will want their posts per page to be custom as well. To do this the easiest way I've come by is writing a filter in functions.php like this:

function portfolio_posts_per_page( $query ) {
    if ( $query->query_vars['post_type'] == 'portfolio' ) $query->query_vars['posts_per_page'] = 1;
    return $query;
}
if ( !is_admin() ) add_filter( 'pre_get_posts', 'portfolio_posts_per_page' );

This method came to me by way of Jonathan Christopher's post called WordPress Posts Per Page Per Custom Post Type. Thank's, Jonathan!

What happens if I don't want my permalink structure to have the same name of my custom post type (e.g. http://company.com/portfolio/)? I'm glad you asked. This is one of the reasons a developer would prefer a Page Template to display their custom post types. It gives the user full control over the permalink structure because they can simply change the name of the Page that's using that Page Template. That's understandable and we'll do that soon, but for those of us who don't need to do that or want another way in the future there's a small edit we can make under the hood to bend that structure to our will.

The function for creating a custom post type, register_post_type(), accepts an argument called rewrite. That argument's value is passed as an array and changing the value of the slug will change the permalink structure. Here's an example:

'rewrite' => array( 'slug' => 'insertyourpermalinknamehere', 'with_front' => true ),

After you've changed this go to Settings > Permalinks and hit the "Save Changes" button to dump your rewrite cache. Visit your site and refresh the page to view the change. Done and done. Again though, the only disadvantage of this method is non-tech-savvy users won't be able to change the name of their permalink structure from the admin GUI unless of course they're headed for the Theme Editor to change that rewrite slug.


Step 3 Page Templates

It's not uncommon for developers to display and paginate their custom post types in a Static Front Page by way of a Page Template. The reason for this is two-fold; it gives the user that permalink structure name-changing ability we just talked about and it enables them to display custom post types on the front page of their site. But things can get ugly here. I'm ashamed to say I once coded custom post type pagination three different ways in one theme. This is partly due to using WP PageNavi, but I own this embarrassing fact to stress that this entire process can be overwhelming for theme developers who aren't in "the know".

Let's call our Page Template page-portfolio.php to match the naming convention for custom post type templates even though it's not one. Here's the code:

/* Template Name: Portfolio */

$paged = 1;
if ( get_query_var('paged') ) $paged = get_query_var('paged');
if ( get_query_var('page') ) $paged = get_query_var('page');

query_posts( '&post_type=portfolio&paged=' . $paged );

require_once( 'archive-portfolio.php' );

This is the first time you're seeing the archive-posttype.php template chained to another template. And it works! But what the heck is going on? This janky $paged variable business highlights the exact problem developers seem to be having with custom post type pagination. Basically if this fix (cough) isn't in place and a user clicks to view page 2, like someone who's been bopped over the head, WordPress gets confused and doesn't know where it's at. And to add insult to injury apparently WordPress knows this and accepts it as normal development procedure by publishing this note in the Pagination Parameters section of the Codex page for WP_Query():

Pagination Note: You should set get_query_var( 'page' ); if you want your query to work with pagination. Since WordPress 3.0.2, you do get_query_var( 'page' ) instead of get_query_var( 'paged' ). The pagination parameter 'paged' for WP_Query() remains the same.

It makes sense to me that developers should be able to set that variable and point to a specific page. What doesn't make sense is why pagination inherently works with default post types (i.e. post, page, attachment), but not with custom ones.

Beyond having to patch the code there is one other catch here in that you can't call your Page slug the same thing as your custom post type slug. Think of your custom post type slug as a reserved keyword; however, you can make the title of your Page the same name as your custom post type slug just as long as your Page slug is something different.


Step 4 Front Page Template

Using the front-page.php template locks users into a custom front page without the ability to change it (unless they delete the file or rename it temporarily). That's why most developers opt for the Page Template method of creating Static Front Pages, but for the sake of my method let's say we're using the former. To achieve custom post type pagination for this template all we need to do is simply include what we've done for archive-posttype.php like this:

require_once( 'page-portfolio.php' );

Step 5 Custom Post Type Taxonomies

The taxonomy-posttype-taxonomy.php template works in much the same way as the front-page.php template, but instead of including the page-portfolio.php template, you'll instead include our index, archive-posttype.php, like this:

require_once( 'archive-posttype.php' );

That completes the extent of my method. Where I was once banging my head against the desk at two in the morning I'm now calmly moving through projects at two in the afternoon. Victory! Well, at least for now. I'm not so naive as to think I may not find myself in a situation where this method doesn't work for custom post type pagination, but I haven't yet.


Conclusion

Unlike my other posts I hope this one becomes outdated. My hope is that WordPress will act on the recent survey that shows 92% of all developers use WordPress as a CMS and take a second look at how pagination works across their platform. The team at WordPress is nothing less than professional. I imagine a future where there's built in pagination tools and possibly a custom post type admin page under Settings for administering posts per page per custom post type. But for now I hope this chaining method helps the many developers I've seen suffering on the WordPress Forums. Please feel free to download, use and abuse the following zipped files as a framework for successful custom post type pagination. Enjoy!

Custom Post Type Pagination Chaining Method Framework (ZIP)

Advertisement