Even if you think you’ve never used WP_Query before, you have, every time you loaded a page in WordPress. In the simplest terms, how WordPress works is when you load a page it creates a WP_Query object that can access the post or posts that the current URL calls for, ie the first ten posts, or 1 specific posts, or the first 10 posts in a category, etc. This allows functions in your theme’s template files, like the_post() and the_content() to output the right post or the right post content.
How that works is not important right now. It’s just important that you know that’s what’s happening. That’s important because it leads to asking the question “hey Josh, what if I wanted to make my own collection of posts, maybe all posts with two specific tags? Or how can I show all posts in a custom post type, ordered by a custom field?” That’s a perfectly natural question for a growing WordPress developer to ask. The answer is you need to learn how to construct and use your own WP_Query objects.
Please note that while what I am about to cover isn’t particularly advanced, it is not the most beginner-friendly topic. I’m assuming in this article that you are familiar with the standard WordPress loop and you’re comfortable opening up a theme or child theme and making a few changes to the PHP code in it.
What’s The This “Object” You Speak Of?
As you might have noticed in the preceding two paragraphs, I keep referring to the WP_Query object. That’s because when you use WP_Query you are using what is called object-oriented PHP. Most people, myself included don’t even realize this when they start using WP_Query.
Think of the WP_Query object as a specially built container for the posts you need and some helpful methods–functions inside that container–for working with them. For example, in a normal WordPress loop, we begin the loop like this:
<pre><?php while ( have_posts() ) : the_post(); ?>
When using WP_Query we start our loop like this:
<?php while ( $query->have_posts() ) : $query->the_post(); ?>
Why? Because we want the posts from a specific WP_Query object, which in this case is stored in the variable $query.
Building A WP_Query Object
As I’ve explained already, a WP_Query object is a container of very specific posts. When we build the object we specify a list of arguments for WordPress to use in building our query. And then we store the object in a variable so we can use it as many times as we need to.
WP_Query has a long list of accepted arguments on its codex page. I can’t teach you all of them in this article, instead my goal is to get you comfortable working with what you see in the documentation.
Here is a basic example, where we ask for all posts with the tag “skywalker” and the category “jedi”:
$args = array( 'tag' => 'skywalker', 'category' => 'jedi', ); $query = new WP_Query( $args );
To break down what I’ve just shown you a bit, what we’ve done, is in the array of arguments, is said to WordPress: “I want posts that have the tag ‘skywalker’ and the category ‘jedi’. Then we take that array of arguments, stored in the variable $args and create a new WP_Query object with it and store that object in the variable $query.
We could expect this query to return posts with titles like Luke Skywalker and Anakin Skywalker.
Looping With WP_Query
Just having a WP_Query object, is by itself not particularly useful. The fact that we can loop through the post it contains, is what makes it a useful thing to have. Looping through WP_Query object is very similar to working with a regular WordPress loop. The difference is that we must do it in context of our WP_Query object. Here is an example, building on the last section:
<?php $args = array( 'tag' => 'skywalker', 'category' => 'jedi', ); $query = new WP_Query( $args ); ?> <?php if ( $query->have_posts() ) : ?> <?php while ( $query->have_posts() ) : $query->the_post(); ?> <h2><?php the_title(); ?></h2> <?php echo the_content(); ?> <?php endwhile; ?> <?php endif; ?> <?php wp_reset_postdata(); ?>
Let’s walk through what we just did. The first step was build the WP_Query object and store it in $query. We’ve already discussed how that works and why. The next step is to check if the query returned any posts. We do this with $query->have_posts() so that WordPress is checking our WP_Query object for posts, not the main WP_Query object that WordPress built automatically when it loaded the post.
Then we use the same looping system that you’re used to seeing in themes to get into individual posts, except we need to do it in the context of the current object, so again we use the current object, followed by the arrow operator (->) and the name of the method–function in the class–that we want to use. Inside of the loop, everything works as usual. We don’t need to do anything special with functions like the_title() or the_content().
We end the loop the same way we always do, but after it is one extra step. That’s using wp_reset_postdata() to tell WordPress to reset itself. This step doesn’t affect the loop it follows, but if we skip this step, it could cause other WP_Query objects, such as those in your widgets to behave oddly. For this reason, ending your WP_Query loop with a reset is important.
When & Where To Use WP_Query
It’s important to know when it is the right time to use WP_Query as well as to make sure you’re not re-creating something WordPress already does for you. For example, if you wanted a page that listed all posts by a specific author, you wouldn’t want to use WP_Query. WordPress already has author archive pages.
On the other hand, what if you wanted your front page template (front-page.php) to have two custom loops? That would be a great use for WP_Query. Let’s say you wanted to show on your front-page the three most recent posts in the portfolio custom post type and then the three posts with the most comments, published in the last month. Those are both excellent uses for WP_Query. I’ll end the article by showing you how to do exactly that, as they are excellent practical examples of how to use WP_Query.
Showing Posts From A Custom Post Type
A few weeks ago I wrote an article here about using the new portfolio custom post type provided by JetPack. Let me show you how to output a link to the three most recent posts in that post type, which is called jetpack-portfolio. First we need to build a WP_Query object, limited to the three most recent posts, in a specific post type. This is what the arguments would look like:
$args = array( 'post_type' => 'jetpack-portfolio', 'posts_per_page' => '3', 'paged' => '1', ); $query = new WP_Query( $args );
What we’ve done here is say to WordPress, I want posts from a specific post type, I want them organized in groups of 3–that’s the ‘posts_per_page’ argument–and I only want the first “page” or group of posts–that’s the ‘paged’ argument.
Now we can build a loop to create the links, using our object that’s stored in $query, like this:
<?php if ( $query->have_posts() ) : ?> <h1>Recent Portfolio Items</h1> <?php while ( $query->have_posts() ) : $query->the_post(); ?> <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> <?php endwhile; ?> <?php endif; ?> <?php wp_reset_postdata(); ?>
Notice that I put the heading for portfolio posts inside of the check to see if we have posts. That way, if there are no portfolio posts, the header isn’t outputted, followed by nothing, which would look funny, If you wanted to add the post thumbnail from the portfolio posts you could with the_post_thumbnail() just like you would in a regular WordPress loop. Have some fun with the markup inside the loop and make it your own.
Showing The Most Commented On Posts
Now, for our final example, let’s show the most commented on post from the last month. The second half of the query arguments for this one are very similar to the arguments from the last example, except we add arguments for ordering the posts by the number of comments a post has.
What makes this arguments array a little more complicated is that we add a date query. That way we limit the posts to the last month. You can skip the date query to show the most commented on posts of all time. Date queries are actually really simple, as the arguments are very readable. As you can see below, we are asking for posts created after 1 month ago and before today. By setting the argument ‘inclusive’ to true returns posts whose dates are from today or exactly one month ago. If we set that to false, we would miss posts from today, which doesn’t make any sense:
$args = array( 'date_query' => array( 'after' => '1 month ago', 'before' => 'today', 'inclusive' => true, ), 'orderby' => 'comment_count', 'order' => 'DESC', 'posts_per_page' => '3', 'paged' => '1', ); $query = new WP_Query($args);
I’ll leave the output to you on this one, as it should be obvious to you know how to work with the WP_Query object.
One thing I did want to mention is that, if you were to use both of these queries one after the other, storing the second one in a variable with the same name as the first will destroy the original query object and replace it with the new one. If you’re done with the first object, that’s a good thing to do, as it clears it from your server’s memory. If you need to hold on to the first object to use later, you can store the second object in a variable of a different name.
I hope this article has helped to open up the wonderful world of WP_Query to you and made it easier for you to read the WP_Query codex page or to read other articles or tutorials that use WP_Query to accomplish a specific goal. Understanding how WP_Query works is important if you want to customize WordPress’ behavior to achieve a specific objective that a site’s design calls for.