WP_Query pagination problems? Here’s a hack

It happens every time I use WP_Query. I get a beautiful-looking custom WP_Query. Then I try to add pagination and everything goes to hell.

I’ve spent hours looking up solutions. Every time. Nine times out of ten, they don’t work. And for no discernible reason. Here’s a hack that worked for me, and is mostly for my own benefit so I don’t forget it next time I am in this situation, but maybe it will help you.

This hack requires Lester Chan‘s plugin WP-PageNavi. Go get it and come back.
For me, this applies specifically to using custom post types. If I wasn’t using CPTs, my WP_Query might not have any problems. Feel free to let me know in the comments if you spot an issue with the way I’m doing things. As I said, this is a hack — meaning it’s not the preferred way to do this — but it’s a functional hack at least.

Let’s start with the query:

			rewind_posts();
			global $wp_query, $paged;
			$temp = $wp_query;
			$wp_query = null;
			$wp_query = new WP_Query();

This is pretty basic. I’m running a rewind_posts() here because I have some custom queries going on in the sidebars, so I threw this in there just to be on the safe side. The next bit is where the hack starts coming in…

			$showposts = 12;
			$offset = ( ( $showposts * $page ) - $showposts );

Here we introduce two variables, both of which we will use in the arguments later. $showposts is a hard-coded rule for the posts_per_page argument. Yes, this is required. The reason will become evident by looking at the next line. Our $offset is being multiplied by the $showposts value (for me, this is 12) and then subtracted by that value again. This is how we are going to fix the issue of not being able to go into paged navigation because it only displays the first page worth of posts — by creating an offset that is multiplied by the current page you are looking at ($page is a global variable that is built into WordPress to display the current page).

Now the $args:

			$args = array(
				'post_type' => 'ap_products',
				'posts_per_page' => $showposts,
				'offset' => $offset,
			);
			query_posts($args);

This is pretty straightforward also. All we’re doing here is declaring the post type (which is just for my use, you can omit this if you aren’t using a custom post type, or replace with your own post type), and then feeding the $showposts and $offset values in here. Then we’re running our query. The full loop looks like this:

<?php the_post(); ?>
	<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">	
	<?php 			
			rewind_posts();
			global $wp_query, $paged;
			$temp = $wp_query;
			$wp_query = null;
			$wp_query = new WP_Query();
			$showposts = 12;
			$offset = ( ( $showposts * $page ) - $showposts ); 
			$args = array(
				'post_type' => 'ap_products',
				'posts_per_page' => $showposts,
				'offset' => $offset,
			);
			query_posts($args); 	?>				
			<div class="entry">
				<?php while ( have_posts() ) : the_post(); ?>				
					<!-- do stuff -->
				<?php endwhile; ?>
				<?php if(function_exists('wp_pagenavi')) { echo '<div class="navigation"><span class="pre-navi"><a href="' . home_url() . '" />1</a></span>'; wp_pagenavi() . '</div>'; } ?>
				<?php $wp_query = null; $wp_query = $temp; ?>
			</div>
		</div>

Notice what I’m doing at the end with the wp_pagenavi function. I’ve added this into my style.css:

.current { display: none; }
.pre-navi { float: left; }

This makes it so that the current page doesn’t display at all. I’m doing this because the current page is always 1 with my fantabulous error, and this way I can replace the page 1 with one that actually links (if you’re on any page other than 1).

And that’s it. It’s not elegant, but it works, and the only real issue is that page 1 is always a link. If you like this solution (or if you hate it), let us know in the comments!

Multiple loops, post formats, and content types in a single home page template

Wow. That title is a mouthful. And given the improbable combination of different content types, I’m actually somewhat surprised that I did, in fact, pull off just that. In working on a client site for a Christian ministry in the Democratic Republic of Ghana, I set out today to create this:

ibm homepage Multiple loops, post formats, and content types in a single home page template

What we have here is a Dynamic Content Gallery at the top, a YouTube video, some static content, and then two different areas for recent posts in different formats. The key here is that everything should be dynamic. We’re not looking to slap a YouTube embed in a home page template; I want to be able to use the new WordPress Post Formats to send the most recent video post to that slot whilst juggling all sorts of other dynamic content from everywhere else and — if at all possible — avoid duplicating anything. Here’s to proving it’s not impossible.

So, the first step is to take care of the DCG. This is the easy part.

12
13
14
15
16
17
	/* first, if we have dcg installed, display dcg inside it's own div */ 
if (function_exists ('dynamic_content_gallery')){ ?>
	<div id="dcg">
		<?php dynamic_content_gallery(); ?>	
	</div>
<?php } ?>

I’ve actually got featured images set up on my other pages in this theme, so if you wanted to do an else for the case where DCG didn’t exist, you could do something like this:

	/* first, if we have dcg installed, display dcg inside it's own div */ 
if (function_exists ('dynamic_content_gallery')){ ?>
	<div id="dcg">
		<?php dynamic_content_gallery(); ?>	
	</div>
<?php } else {	
// if you don't have dynamic content gallery, maybe a featured image would work?
// this is borrowing the code pretty much straight from Twentyten, so you'll need to add this to your functions.php, too
// so check with Twentyten and get that sorted first
	if ( is_singular() && current_theme_supports( 'post-thumbnails' ) &&
			has_post_thumbnail( $post->ID ) &&
			( /* $src, $width, $height */ $image = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'post-thumbnail' ) ) &&
			$image[1] >= HEADER_IMAGE_WIDTH ) :
		// Houston, we have a new header image!
		echo get_the_post_thumbnail( $post->ID );
	elseif ( get_header_image() ) : ?>
		<img src="<?php header_image(); ?>" width="<?php echo HEADER_IMAGE_WIDTH; ?>" height="<?php echo HEADER_IMAGE_HEIGHT; ?>" alt="" />
<?php } ?>

(Note: this part isn’t tested, per se…I’m just combining two different things I have going on in this theme. It should work, but if if borks your theme let me know, and I’ll fix my code.)

Okay, back to my code.

After DCG, I’ve got a video and static content. The static content is going to be whatever they have on their Home page. The video is the most recent video post. We’re using Post Formats so — like the featured image snippet above — you’ll need to enable that in your functions.php to get that working before you try this.

19
20
21
22
23
24
25
26
<div class="content home">
<?php /* this is the main home page stuff...there's a lot going on here 
	     first of all, we're displaying the most recent VIDEO post */ 
	query_posts( 'showposts=1' );
		if (have_posts()) : while (have_posts()) : the_post(); 
			if (has_post_format( 'video' )) { ?>	
				<div <?php post_class(); ?> id="post-<?php the_ID(); ?>"><?php the_content(); ?></div>
        <?php } endwhile; endif; rewind_posts();

So far, so good, and nothing too complex yet, right? All we’re doing here is running a loop that’s limited to 1 post, and making sure that post is a video post. Here’s where it gets fun.

27
28
29
30
31
32
33
34
35
		/* next, we're displaying the regular home page content.  That's right, you heard me, we're adding page content in between a bunch of dynamic post stuff */
		$ibm_home_page = new WP_Query('post_type=page&name=home'); 
		while ($ibm_home_page->have_posts()) : $ibm_home_page->the_post(); ?>
			<div <?php post_class('home-content'); ?>>
				<?php the_content(); ?>
			</div>
		<?php endwhile; rewind_posts(); ?>
	<div class="clear"></div>
	<hr />

I’ve completely switched over to WP_Query to be able to grab a Page with the slug ‘home’. But since I did rewind_posts, the Loop isn’t completely freaking out. Underneath this, we’ve got even more fun times.

36
37
38
39
40
41
42
43
	<?php /* down here, we're doing two more things.  First order of business is to display the next post making sure that the next post ISN'T the video post above. we're also excluding sticky posts here to get a more linear order going on. */ 
	query_posts( 'caller_get_posts=1&showposts=1' );
		if (have_posts()) : while (have_posts()) : the_post(); if (has_post_format( 'video' )) { query_posts('offset=1&caller_get_posts=1&showposts=1'); } else { ?>
			<div <?php post_class('home-excerpt'); ?> id="post-<?php the_ID(); ?>">
				<a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><h2 class="the_title"><?php the_title(); ?></h2></a>
				<?php the_excerpt(); ?>
			</div>
		<?php } endwhile; endif; rewind_posts(); ?>

Woah there. It was at this point that I started running into a couple snags. First of all, I kept getting any sticky posts popping up at the top of the list, above my single post I was trying to display. This is only supposed to be a single post excerpt, so that was not going to work. Second, the next post, under my sticky post, just happened to be my video post which I created to test the video Post Format. Which is ridiculous, right? You don’t want your video and then a big empty space under your video (because I’m using the_excerpt rather than the_content) with the title of the post for…your video. And that would be bound to happen anytime you posted a video. Ninja coding skills needed to be summoned. Or, barring that, ninja Googling skills…

The first half of the query is straightforward. caller_get_posts gets the next posts in the loop sans sticky posts. Golden. And we only want one, so that part’s obvious. Next, after we do our normal loop stuff, I throw in this if (has_post_format( 'video' )) stuff that looks like it does pretty much the same thing. Well…it does. With one exception: if the next post is a video post, we’re going to offset the query by one and grab the one after that, effectively skipping your video. It’s ugly but it works, and that’s all I wanted.

This last bit is just recent updates. With all the querying and offsetting, I know we’re going to have some issues here. But I don’t care too much about this section because all I’m looking to do here is display some links from some recent posts. On another site, I might do random posts or popular posts or most commented posts here, and none of the stuff we’ve done up to now would even matter for these. Instead, what I’m doing is adding an extra offset, and if I skip a post, no big deal — chances are, that post will be featured in the DCG above, anyway.

44
45
46
47
48
49
50
51
52
53
	<div class="home-updates">
		<h2>Recent Updates</h2>
		<?php /* finally, this last section is just a recent posts area.  we care a bit less here about sticky posts or video posts, so really the only thing I'm concerned about is the offset, which i've incremented by 1 extra just in case.  this may result in some duplicates, or, alternately, some posts missing, but, again, they're just links and they're not taking up much real estate and I'm not too concerned about it */
		query_posts( 'showposts=5&offset=2' );		
			if (have_posts()) : while (have_posts()) : the_post(); ?>			
				<a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><strong><?php the_title(); ?></strong></a><br />
			<?php endwhile; endif; rewind_posts(); ?>
	</div>
</div>    
<div class="clear"></div>

And there you have it. The real star of the show here is rewind_posts. If you’ve ever done multiple loops before rewind_posts came along, you’ll know that all sorts of wacky stuff can come up that throws your loop(s) off. rewind_posts allows you to use multiple loops in the same template, and then go on and do some more loops somewhere else, and nothing goes horribly awry.

Hopefully this gives someone out there some ideas of what you can do with multiple loops using query_posts. There isn’t a lot of stuff out there yet on Post Formats — particularly excluding post formats, so that was one area I sort of had to wing it. If anyone has some awesome ideas, I’d love to hear them.

(By the way, all the example text used in the mockup above comes courtesy of F*** Lorem Ipsum, which I’ve been using as an alternative to the Lorem Ipsum Generator I’ve used for years. F*** Lorem Ipsum pulls text from TV shows and commercials and is able to throw in links and line and paragraph breaks, too.)