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.
Query On
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.
Wow, thanks a ton! I knew this had to be possible, but being new to WP I hadn’t gotten to this point. For years I created sites and apps coding by hand, using ColdFusion as a server-side language, familiar with PHP but never fully learning it. Recently I’ve been trying to duplicate some functionality I had developed for a client years ago in ColdFusion, and was hoping to find a plug-in for WP that would do most of what I wanted, and then fudge the rest best as I could. This should go a long way in helping me pull the data I need when I need it.
Great post. Thanks
This was such a fantastic blog post, Josh. I learned a lot and am looking forward to using this in my web development.
You explain things so clearly and make them accessible to someone like me who mostly cuts and paste from code others have written.
Thank you!
Simply brilliant !!
Thanks.
Great post.Thanks for sharing such a useful information.
Probably the best explanation I’ve seen. Great work!
Great post, thanks for sharing this post with us.
A-W-E-S-O-M-E. Thank you.
Great article. Thanks fro sharing this post.
Hi there.
Love how the WP_query gets smarter everyday. I don’t have to know sql now to be able to fetch something special.
I’d like to point out that it would’ve been great to have the link to the latest version of the codex π
Thanks a lot!
Yep, i must of read 20 odd different pages about WP_query, and not one single one tells you WHERE to use them.
Where does it go? Functions.php? post.php singlepost.php ect
The best explanation in simple way ! Thanks a lot for this brilliant post.