Getting to Know WP_Query

The WP_Query class is an exceptionally powerful tool in WordPress. As you may know, every WordPress site contains a database that stores many posts (of many post types) that make up the bulk of that site’s content, and WP_Query is the best way to fetch or retrieve a given selection of those posts for processing. So your theme files use it on regular basis, and a lot of plugins on your WordPress site will it.

The use and power of WP_Query will make more sense as we get into the details, so let’s get started!

WP_Query and Object-Oriented PHP

WP_Query is a PHP class that has certain properties. When you say, in PHP, new WP_Query() you create an object (or instance) of that class.

If object-oriented PHP is a new concept, you may want to read our introduction to the topic. If you’ve read it, or already understand the basics of object-oriented PHP, let’s apply those concepts to WP_Query.

Getting The WP_Query Object You Want

When you make a new WP_Query object, you create a query that will pull from the WordPress database all the posts you request.

When you make a new WP_Query object, you create a database query that will pull from the WordPress database all the WordPress posts you request.

So one of the first things you need to understand about WP_Query is that by default, when you just say new WP_Query, you don’t get much use from it. Rather, the way you create your object — the specific query you run — is how you harness the power of WP_Query.

The Gutenberg Bible

The WordPress Codex. (But actually a Gutenberg Bible.)

The “constructor” or initializing method of WP_Query takes just one parameter, $args. This is common in WordPress — you just pass an array or string of $args that contains a rich specification of what you want back. The depth of what’s possibly to put into your $args are well beyond what would be valuable to share here, but we’ll cover the basic points. When you need to make a specific query for a specific purpose, I recommend you just consult the Codex.

The $args parameter accepts both arrays and strings, and the syntax is slightly different between them. I recommend you mostly build your $args as arrays, because they can take you way further and with less mental weight than strings will.

A WP_Query Example: Get All Link Posts I Wrote Recently

The Codex recommends that you can get all posts from a specific user with this syntax:

$query = new WP_Query( 'author_name=rami' );

As I mentioned, though, I’ll be using arrays, because it’s much easier to keep track of everything with a well-formatted array than a convoluted string.

So to write a query to find posts I’ve written, I’ll start with the following:

$query = new WP_Query( array( 'author_name' => 'david' ) );

If you’d like a bit more on array syntax, check out my introductory tutorial on PHP arrays.

Our full goal here is to pull a set of all the Link posts I’ve written here on WPShout in the last week. To do that, I create the following query:

$query_args = array(
    'author_name' => 'david',
    'post_type' => 'post',
    'tax_query' => array(
            'taxonomy' => 'post_format',
            'field' => 'slug',
            'terms' => array( 
    'date_query' => array(
            'after'     => date('F j, Y',(strtotime('7 days ago'))),
            'inclusive' => true,
    'posts_per_page' => -1,
$query = new WP_Query($query_args);

What this does is quite deep, but I’m diving in with both feet to show you the depth your post queries can have when you get a grasp of WP_Query. The first line of our $query_args array we discussed in the first example — it constrains our results to my posts.

It’s also important to realize that query elaboration in WP_Query is an and relationship. So every new criteria I add limits the number of posts that I get back by the specified criteria. And my first new limit is that I’m specifying that I only want Posts, not any other post (aka: content) type. This is technically redundant, but better safe and explicit than simple and confused I say.

wordpress-post-formatsThe next bit — the tax_query constrains us to to the Link post format. Post formats are one of the more awkward parts of WordPress, and this query does seem to show it. In short, Post Formats are technically just a WordPress taxonomy, like tags or categories, and so to limit yourself to a specific format you need to use that in your argument. This is the specific format that the Codex recommends to get Post Formats.

Next, I only want posts from the the last 7 days. The new Date Query features added in WordPress 3.7 are great, but I’m just scratching the surface of them here.

Finally, with 'posts_per_page' => -1 we’re telling WordPress not to paginate — give us only a subset of all it finds — because even if there were 250 posts, I want them all. Sometimes the pagination default on a specific site is quite low, and you’ll want quite a few results, and you can get quite confused if you forget that WP_Query paginates by default.

Using the Results of Our WP Query Example

There’s a cool secret hidden inside of WordPress, and it’s related to this WP_Query object we’re getting at through the above section. What is it? It’s that we’re already familiar with working with the WP_Query object if we’re at all comfortable with “The Loop”, which I’ve described in detail, and in a feature about the “Three Core Concepts of WordPress Themes.”

So whenever you’re inside while ( have_posts() ), you’re always working with a WP_Query object, it’s just been hidden from you. So the way you work with your new one is not foreign at all. To cycle through the results as you’re used to doing in template files for your theme, the process is very similar as it is in the default Loop:

$query = new WP_Query( $query_args );

while( $query->have_posts() ) {
	echo '<li>' . get_the_title() . '</li>';

// Restore original Post Data

What that means is that when you’ve been doing the whole while have_posts() dance, you’ve been working with a WP_Query object all along, it’s just been hidden from you. So the way you work with your new one is not foreign at all.

That while loop, and the priming of the_post() are like in the default Loop, except that we’re being explicit about which WP_Query object we’re applying it to — our own custom query, which we’ve named $query.

Inside of our loop of our object, we’re marking these post titles up into a simple HTML list. You can do a ton more formatting, and use all the template tags you’re used to using inside the Loop.

Really, the only new and important detail here if you know The Loop is the call to the the wp_reset_postdata() fuction on the last line. This is necessary in alternative loops because WordPress relies on a lot of global variables, and if you don’t do this, weird things may happen with things like your is_category() calls elsewhere in the page. So it’s good practice, as Brian Krogsgard describes, to always go ahead and end any custom uses of WP_Query object with a call to wp_reset_postdata.

We’ve Only Just Begun… to Know WP_Query

I hope you feel like you now understand what the WP_Query class and and objects are, how you can use them to get just about any content you want out of WordPress. There are so many more selection criteria available than we’ve touched on so far to. Chances are good that if you ever think “I’d like to get posts where…” a trip to the Codex to find out the specific way that works inside WP_Query will get you going. You can filter by any taxonomy, any custom field, any post status, and even whether or not the post is password protected.

A final note: We’ve not addressed it so far, but you may be familiar with the function query_posts(). This can seem like a valuable shorthand, but it’s not a good idea to use. Here’s a great explanation from Andrey Savchenko (aka Rarst) about why.

We’ve barely scratched the surface about all the intricacies and possibilities of WP_Query in your work with WordPress. If you’re ready for a bit more depth, I recommend highly this talk from Andrew Nacin: “You Don’t Know Query.” (I discovered that from the great post mentioned above from Brian Krogsgard.) What do you feel like you still don’t know about WP_Query? I hope less than before!

Image credit: Andrew Beeston, starfire2k