Creating WordPress Themes: query_posts();

April 28, 2009

Now we’re getting into the good stuff. The problem is, the “gooder” the stuff, the harder it is to provide you with a good tutorial that makes sense! We’re now getting into the parts of customization where you really need to have an understanding of what’s going on, because what I’m about to describe to you can be used in many, may different ways – there’s no way I could possibly give you a tutorial on every single way of using this.

However, what I am going to do is try to explain this in plain English, and give you the most oft-used examples (for me, anyway) and hopefully the light bulb over your head will switch on.

query_posts(); is, indeed, one of the best things you can learn when it comes to using WordPress. By default, the Loop is a query in and of itself. I’ve described the Loop before, but in case you missed it, it’s basically a default WordPress function that “queries” the database, and asks for certain information. It them delivers said information over and over again, as long as there are results to the query.

Now, to put that in plain English, think of it like a collating copy machine. You place a stack of papers on the machine, ask it to “collate” (i.e. make copies of the whole stack, but keep them in order), set the number of copies you’d like to have and hit the big green button. Next thing you know, the copy machine is sucking in the pages and spitting out the copies in order, in little groups for each copy.

This is a really simple illustration of what it is that the Loop does. You have content placed into your database (the stack of papers), you set the machine to pull the content in and output it in a certain way (the query), and you can even set how many you want to come out (this is in your Settings>Reading area: “Blog pages show at most X pages”). By default the Loop simply queries everything, and pulls out the latest 10 posts to stick on your index page. But what if you’d like a little more control over this?

That’s where query_posts(); comes in.

query_posts(); basically allows you to alter the default query. You can choose to output more specific information: like the most recent post in a particular category, or the contents of a particular Page, or even the most recent post that’s been commented on. There’s all kinds of possibilities for this little command. Most of my clients get this neat little function implemented when they want, say, some introductory text on the index page that is still editable, but mostly static (changes only when they deem it necessary to do so), or when they want a particular section or two to show up on the index page. This site actually uses queries all over the place. My “Hello” section at the top of the index page is actually a Page in the back end, as are the “About,” “Services” and “Contact” Pages. The three sections at the bottom are actually three different categories I have.

The only thing is that my index pages – again – uses queries all over the place. So I’m using a different version of query_posts(); called new WP_Query(); . Although they both do pretty much the same thing, there is a difference, and I’ll explain it to you – but let’s get the query_posts(); stuff out of the way first.

So basically, the use of query_posts(); just makes alterations to the regular ol’ basic Loop. As a minimal example, say you’d like to pull in the most recent 3 posts into your index page, but have the regular 10 show up everywhere else. You’d simply apply the following code to your index.php file (or home.php – if you’re using that):

<?php query_posts('showposts=3'); ?>

Now, keep in mind, this must to go before the default Loop – you’re redefining the Loop, so you need to tell it what to do. (If you put it inside the Loop, you’re telling it to do something after the fact. That’s like yelling “Stop!” to the driver after the car’s been totaled.) So, as another example, say you want the most recent post out of category 6 to show up:

<?php query_posts('showposts=3&cat=6');
if(have_posts()) : while(have_posts()) : the_post(); ?>

And there you go.

There’s a whole list of different parameters you can apply to your query to pull off all kinds of things you’d like to do. However, there’s an important note you should keep in mind: when you use query_posts();, you’re resetting the default query. This means that if you call any other Loops, they WILL, indeed, follow the parameters of the initial query. Yes, this will even affect the sidebars. If you’re pulling in the index.php file, and there’s a custom query, then any Loops used after that query will utilize the effects of that query – meaning if you have a query in the sidebar, and your sidebar.php call is after the one in index.php – then your sidebar will use that query too and cause you major headaches from beating your head on your desk.

There’s a couple of ways around this. One is to specify that the query is temporary:
in PHP5, objects are referenced differently than in PHP4, so there’s a slight edit to this query reset if your server is using PHP5 – you’ll have to clone rather than copy. So the first line would be $temporary = clone $wp_query; , and the last would be $wp_query = clone $temporary;<?php $temporary = $wp_query; // set the defined new query
query_posts('showposts=3&cat=6');
if(have_posts()) : while(have_posts()) : the_post(); ?>
your post layout here
<?php endwhile; endif;
$wp_query = $temporary; //reset it back to normal ?>

But my preferred method is to use the new WP Query();. Now, this is a personal preference, and I like it because I feel like I can see what’s going on in my code a bit better, because I can name things. It’s basically the same function as query_posts();, but a slightly different application, and it removes the necessity of cloning/resetting. To me, it seems cleaner and neater.

And example, of the new query would be… say you want a static Page (we’ll say “welcome” is the title) to appear at the top of your index page, and just below it, you want the last 5 posts in a particular category (we’ll say “6″). The code you’d place in your index.php file (or again, home.php) would be like so:


<?php $welcome = new WP_Query('pagename=welcome');
if($welcome->have_posts()) : while($welcome->have_posts()) : $welcome->the_post(); ?>
your post layout here
<?php endwhile; endif; ?>

<?php $cat6 = new WP_Query('cat=6&showposts=5');
if($cat6->have_posts()) : while($cat6->have_posts()) : $cat6->the_post(); ?>
your post layout here
<?php endwhile; endif; ?>

Now, you’ll notice that I 1) named the query/set it as a variable; and 2) I called that variable within the default Loop, so it know that I only want this specific thing to happen. Because I’ve done this, there’s no need for a reset. It’ll look ONLY for what I’ve set in that variable, and when that particular Loop is finished, it’s done. The query is not retained, and life goes on as usual. Using the above method, you can use as many queries on a page as you want. (Of course, you can do it with the previous method as well, but you just need to remember to reset your Loop – like I said, I just fine te latter a more pleasant way of doing it.)

And finally, I’ll provide one final example – simply because this one’s really cool. Say you’d like to have the most recent post of a particular category on the index page, in the main content area – but you’d like the most recent 3 posts after that one to show up after the initial one, without duplicating it (say you want to style the first one differently or something). There’s two ways of doing this: one is to call in a query on the index.php page for the initial post, and then duplicate it below that (using offset):

<?php $cat6 = new WP_Query('cat=6&showposts=1');
if($cat6->have_posts()) : while($cat6->have_posts()) : $cat6->the_post(); ?>
your post layout here
<?php endwhile; endif; ?>

<?php $sidecat6 = new WP_Query('cat=6&showposts=5&offset=1');
if($sidecat6->have_posts()) : while($sidecat6->have_posts()) : $sidecat6->the_post(); ?>
your post layout here
<?php endwhile; endif; ?>

You’ll note the addition of “offset” – which will make it skip the first post and start with the next. But this is way cooler (and a little more reliable) because it actually queries the ID that’s showing and retains it’s value to be used elsewhere:

<?php $cat6 = new WP_Query('cat=6&showposts=1');
if($cat6->have_posts()) : while($cat6->have_posts()) : $cat6->the_post();
$no_duplicate = $post->ID;?>
your post layout here
<?php endwhile; endif; ?>

<?php $sidecat6 = new WP_Query('cat=6&showposts=5&offset=1');
if($sidecat6->have_posts()) : while($sidecat6->have_posts()) : $sidecat6->the_post();
if($post->ID == $no_duplicate) continue;
update_post_caches($posts);?>
your post layout here
<?php endwhile; endif; ?>

So what the above does is basically grab the post ID that’s showing in the first query, and retains it as a variable ($no_duplicate). In the second Loop, it’ll start the query anew BUT if a post ID within this second Loop matches the $no_duplicate post ID, then it’ll skip it, update the post cache, and go on its merry way.

Hopefully that was something really useful for you, and I’ve explained it in such a way that you can “get” what’s going on.

Up next: Split Navigation

View Comments

your‘s post is very neat & serious.
i have subscribed it.thank you of your hard work.

Great post!!!

Would you know how to grab the two most recent posts (with excerpt), then show the next three post titles (Just the Titles)?

Thank you!

Yes, it’s the same principles as above. There’s a couple of ways to do it – the simplest would probably be to use *two* queries – one for the first two and a second for the following three. But the most *efficient* method would be to do a single query and pull in the 5 posts, and use the count method to use a flag so the display is edited. For example, say you want category 3 to be the posts you pull. You’d do this:

<?php $cat = new WP_Query('cat=3&showposts=5');
$count = 1;
if($cat->have_posts()) : while($cat->have_posts()) : $cat->the_post();
$no_duplicate = $post->ID;

if ($count < = "2") { ?>

do your layout code with the excerpt for the first 2 posts

<?php } else { ?>

do your layout for the titles only

<?php $count++; endwhile; endif; ?>

That basically counts each post that you’re pulling in, and if the count is “1″ or “2″, then it’ll output your layout with the excerpt, but if it’s not, it’ll do titles only.

Just so you know, that’s off the top of my head, so I might have a typo or two in there, but I’ve used a similar method to insert stuff between posts (like ads, line breaks, etc.) so I do know it works. This is actually something i plan to cover in detail in a post that coming soon, as well – but that should get you started :)

Sensational post. It solved exactly the problem I was having in my index page, where I am mixing the two kinds of loops. Thank you so much,
João

Nice!
Why would you opt for the $post->ID way when youre passing the same argument – as the non $post->ID version – to the WP_Query constructor

well, using the $post->ID basically just grabs the current post ID and retains it as avariable. Then in the next loop, it uses it as a flag: if the post ID from before matches the ID of the posts that’s being Looped, then it’ll ignore it. (The second query Loops through allt he posts again, so it’ll find the post that’s already displayed by the first query, and display it unless you tell it not to.)

An alternate version to this is to simply use “offset” – then if you’re displaying the first post from a category in the first query, you can just “offset=1″ in the second, and it’ll just skip the first post.

Thanks very much for posting this, made it much easier to understand. I’ll be popping your RSS feed into my Reader, thanks very much.

Sorry, comments are now closed on this post. You may thank the spammers for that one. But if you have any questions, please feel free to email me and ask - maybe it'll make for a good update in a future post. :)