At the heart of the WordPress theme template is the venerable WordPress loop. When you're looking at your
index.php file, for example, the loop is the part that typically begins with
if(have_posts()) and contains all the tags and markup used to generate the page. The default loop works perfectly well for most single-loop themes, but for more advanced designs with stuff like multiple and custom loops, more looping power is needed. Fortunately, WordPress provides plenty of flexibility with four ways to loop:
Each of these looping methods is useful in a variety of situations. They share a lot of the same underlying functionality, and the query parameters are essentially the same. Collectively, these four techniques enable simple loops, multiple loops, and custom loops in your WordPress theme template. A good place to find a default loop, for example, is in your theme's
index.php file. Its purpose is to loop through the posts stored in the database and echo their contents to the browser. Using WordPress' template tags, it's easy to display post titles, content, meta info, and much more. That said, let's examine the four ways to loop with WordPress.
The Default Loop
The default WordPress loop looks something like this:
<?php if (have_posts()) : while (have_posts()) : the_post(); ?> <div <?php post_class(); ?> id="post-<?php the_ID(); ?>"> <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1> <?php the_content(); ?> </div> <?php endwhile; ?> <div class="navigation"> <div class="next-posts"><?php next_posts_link(); ?></div> <div class="prev-posts"><?php previous_posts_link(); ?></div> </div> <?php else : ?> <div <?php post_class(); ?> id="post-<?php the_ID(); ?>"> <h1>Not Found</h1> </div> <?php endif; ?>
So what makes it "default"? Mostly because it uses the default query to loop through post content, making it the loop used like 99% of the time for most themes. It tells WordPress to loop through posts and display the information according to context, and as called by the various template tags (
the_content, et al). There are tags available for just about any type of data stored in the database.
Based on the query that is sent, the default loop will display a certain number of posts from a certain category from a certain date, and so on. For example, the number of posts displayed in the first part of the loop is specified in the WP Admin. So if someone requests the second page of your "Awesome" category, that information is sent via the query, along with the number of posts, theme template file, and so on.
So the default loop is perfect if you're happy with the query that is sent, but it is also possible to customize the query and generate an entirely different set of posts.
Loop with query_posts()
The query_posts function enables us to modify the query and display our desired results. We can either override the entire query or keep it around and just change a few parameters. Here's an example where
query_posts is called before the default loop to exclude a specific category:
<?php global $query_string; // required $posts = query_posts($query_string.'&cat=-9'); // exclude category 9 ?> <?php // DEFAULT LOOP GOES HERE ?> <?php wp_reset_query(); // reset the query ?>
Say you have a default loop in your
index.php theme template, but you want to change the number of posts, exclude two categories, and display the results in ascending order. Easy. Just add some
query_posts action before the default loop and
wp_reset_query immediately after, like this:
<?php global $query_string; // required $posts = query_posts($query_string.'&posts_per_page=3&cat=-6,-9&order=ASC'); ?> <?php // DEFAULT LOOP GOES HERE ?> <?php wp_reset_query(); // reset the query ?>
Here we are keeping the original query around and just overriding a few parameters. There are many parameters available, so customizing any default loop is accomplished quite easily. If we wanted to completely override the original query, we would replace the second line with something like this:
$posts = query_posts('posts_per_page=3&cat=-6,-9&order=ASC');
Notice here that we've removed the
$query_string from the
query_posts parameters. This essentially erases the default query and replaces it with one that contains only those variables included in
query_posts. This means no paging information will be available, so remove the original query only if you know what you're doing.
When to use? Use
query_posts to modify the type of posts that are returned for a single loop. It’s perfect for limiting the number of posts, excluding posts from a certain category or tag, and so on. If more than one loop is required, multiple
query_posts loops could work, but there is a much better way to do it using
Loop with WP_Query()
For complete control over the customization of any number of loops, WP_Query is the way to go. When used to modify a default loop, it looks similar to
query_posts. For example, let's exclude a specific category using
<?php $custom_query = new WP_Query('cat=-9'); // exclude category 9 while($custom_query->have_posts()) : $custom_query->the_post(); ?> <div <?php post_class(); ?> id="post-<?php the_ID(); ?>"> <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1> <?php the_content(); ?> </div> <?php endwhile; ?> <?php wp_reset_postdata(); // reset the query ?>
It also accepts the same parameters as
query_posts, so modifying stuff like number of posts, included/excluded categories, and post order looks quite familiar. As seen in the following examples,
WP_Query makes it easy to customize the loop by simply changing the parameter:
$custom_query = new WP_Query('cat=-7,-8,-9'); // exclude any categories $custom_query = new WP_Query('posts_per_page=3'); // limit number of posts $custom_query = new WP_Query('order=ASC'); // reverse the post order
As expected, we can combine parameters with
WP_Query using the same syntax as both
get_posts. Here’s the equivalent of our
$custom_query = new WP_Query('posts_per_page=3&cat=-6,-9&order=ASC');
Notice, however, that with
WP_Query, we don’t need the
$query_string variable. In addition to using
WP_Query to customize the default loop, we can also use it to create and customize multiple loops. Here is a basic example:
<?php // Loop 1 $first_query = new WP_Query('cat=-1'); // exclude category while($first_query->have_posts()) : $first_query->the_post(); ... endwhile; wp_reset_postdata(); // Loop 2 $second_query = new WP_Query('cat=-2'); // exclude category while($second_query->have_posts()) : $second_query->the_post(); ... endwhile; wp_reset_postdata(); // Loop 3 $third_query = new WP_Query('cat=-3'); // exclude category while($third_query->have_posts()) : $third_query->the_post(); ... endwhile; wp_reset_postdata(); ?>
Each of these additional loops may be placed anywhere in your theme template – no need to line them up sequentially. For example, one loop may be placed in your sidebar, another in your footer, and so on. And with the output of each loop easily modified using any of the available parameters, any loop configuration is possible.
When to use? Use
WP_Query for creating multiple, customized loops. By setting up additional instances of
WP_Query in your theme, you can create any number of multiple loops, and customize the output of each.
Even so, we don't always need to break out the big guns, sometimes we just need a few additional loops displayed around the page. So let's put down the bazookas and gather in the garden for some
get_posts tea ;)
Loop with get_posts()
The easiest, safest way to create multiple loops in your theme is to use get_posts(). Anywhere you need to display a quick, static set of posts,
get_posts is the perfect choice. Think 10 recent posts in the sidebar, or 10 random posts in the footer.
get_posts makes it easy. Here again is a query to exclude a specific category:
<?php global $post; // required $args = array('category' => -9); // exclude category 9 $custom_posts = get_posts($args); foreach($custom_posts as $post) : setup_postdata($post); ... endforeach; ?>
This code creates a loop of all posts except those in the excluded category. Of course, excluding a category is just one way to customize your additional, static loops. By using any of the same parameters accepted by
query_posts, it's possible to create loops that display just about anything you want.
Notice, however, that
get_posts requires the use of an array for the parameters. The format for multiple parameters looks like this (using our previous example):
$args = array('numberposts'=>3, 'category'=>-6,-9, 'order'=>'ASC');
Also notice that we're using
numberposts instead of
posts_per_page to limit the number of posts. According to the WP Codex,
posts_per_page should work with
get_posts, but if it doesn't just go with
numberposts. There is also a
showposts parameter that also seems to work fine with
When to use? Use the
get_posts() function to easily create additional, static loops anywhere in your theme.
get_posts accepts the same parameters as
query_posts, and is perfect for adding static, custom loops to your sidebar, footer, or anywhere else.
The bottom line for customizing default loops and creating multiple loops:
- To modify the default loop, use
- To modify loops and/or create multiple loops, use
- To create static, additional loops, use
If you're working with WordPress loops and want to learn more about using them to customize your theme, we cover the topic extensively in our book, Digging into WordPress, which is current with WordPress 3.1 :)