WordPress loops, latests posts, sticky posts and problems

I just figured out a problem (or just a nuisance?) of WordPress 3.0.1 that was driving me crazy, but finally got it working as expected. To all you WordPress theme developers out there, I hope the following explanation of the problem and the solution will save you a few minutes or hours of head-scratching.

Problem description

You decided you want to show the latest post, at some place within your theme. You’ve just crafted a beautiful loop for that job, dressed with the appropriate markup. You have probably done this a million times before, and you are sure it’s bound to work.

If you are like me, you’ve probably done something like this (simplified and unstyled for convenience):

<?php $one = new WP_Query( array(
	'posts_per_page' => 1
)); ?>

<?php while ( $one->have_posts() ) : $one->the_post(); ?>

	<?php the_date(); ?>
	<?php the_excerpt(); ?>

<?php endwhile; ?>

Pretty straightforward some would think. All I do is, construct a new WP_Query object (which gets posts by default), stating that I want only one post per page, using the posts_per_page parameter.

So, you save your file, refresh your browser, and you see three (or any other number of) posts instead of one. Crap!

So, what went wrong?

Explanation

After some investigation, I found out that posts_per_page doesn’t play nice with sticky posts. In detail, my most recent posts on my testing installation were:

  1. Post E – sticky
  2. Post D – sticky
  3. Post C – not sticky
  4. Post B – not sticky
  5. Post A – sticky

and my loop was returning Posts E, D and C, when all I wanted was Post E, which is the most recent. It turns out that posts_per_page is satisfied only when non-sticky posts are encountered.  This is probably an undocumented behavior and not a bug, but it really doesn’t abide to commonsense rules.

Let’s suppose I misunderstood something and this is expected behavior. One would expect the posts returned to be:

  1. The sticky posts, all of them, as they are sticky and should always be on top.
  2. The number of the non-sticky posts I asked.

In that sense, I believe it’s a WordPress bug, as it returned Posts E, D and C, but not Post A which is also sticky and should be on the top. I would expect Posts E, D, A and C.

Now, let’s suppose that I understand absolutely everything and my loop isn’t wrong. Shouldn’t it return just one post? Even if it’s the sticky one. If Post E wasn’t sticky, it should return post D which is not what I wanted but is pretty much expected.

Solution

While in the head-scratching session and out of desperation, I tried any possible solution. A loop with query_posts, with get_posts, with WP_Query… and passing various parameters to them.

And finally, just before I gave up, that one last parameter I tried, worked!

caller_get_posts=1

Of course, it makes perfect sense. caller_get_posts=1 makes sticky posts behave as if they are not sticky, so they don’t interfere with my loop. The loop now returned only Post E, which is what I wanted and is in fact the latest post. All I had to do is pass the caller_get_option=1 to my query, along with any other parameters.

So, for completion, here is the working code:

<?php $one = new WP_Query( array(
	'posts_per_page' => 1,
	'caller_get_posts' => 1
)); ?>

<?php while ( $one->have_posts() ) : $one->the_post(); ?>

	<?php the_date(); ?>
	<?php the_excerpt(); ?>

<?php endwhile; ?>

I hope this saves you a few minutes. Let me know if it helped you.

P.S. I purposely didn’t mention that I forgot all about my sticky posts while scratching my head as to why my loop wouldn’t work properly, and although relevant to my specific situation, doesn’t really fit with the rest of the problem described in this article.

Update – It seems this has been reported to the WordPress Bug Tracker as ticket 9300, and occurs from as early as version 2.7. At the time of writing, the current version 3.0.1 still suffers from the same problem.

You may also like

1 Comment

Leave a Reply

Your email address will not be published. Required fields are marked *