Using WP_Query In WordPress

Advertisement

If you’ve been around WordPress for a while, you’ll know how difficult it used to be to create lists of posts based on complex criteria while also conforming to WordPress’ standards. Over the course of a few years, the platform has come a long way. By using the power of the WP_Query class, we can lists posts in any way we want.

wp_query (1)1

What Is WP_Query?

The WP_Query class is one of the most important parts of the WordPress codebase. Among other things, it determines the query you need on any given page and pulls posts accordingly. It also saves a lot of information about the requests it makes, which helps a great deal when optimizing pages (and troubleshooting them).

The other role of WP_Query is to enable us to perform complex database queries in a safe, simple and modular manner.

Safety

Throughout our interactions with this object, we are supplying parameters and using functions to reach our goal. The object’s internals take care of many annoyances, such as protecting against SQL injection attacks and making sure that proper data types are used.

Simplicity

The object abstracts much of the query complexity away so that we don’t have to muck about with the specifics of our database. Because we are using an array to supply our criteria, everything is more self-explanatory. No manual database joins or nested queries are needed — just create an arguments array and instantiate the class!

Modularity

My favorite of all is modularity. When making raw queries, it is hard to manage those frequently used bits because they are just fragments of SQL code. WP_Query does away with this by using an associative array as an argument. A plethora of goodness ensues: you can merge arguments from different places, run array functions to your heart’s content and manipulate it in ingenious ways.

Getting Started

The “Regular” WordPress Loop

Let’s look at a regular loop first, and then create the same loop using WP_Query. Let’s assume that we’re coding in the category.php file.

<?php
   if(have_posts()) : 
      while(have_posts()) : 
         the_post(); 
?>

         <h1><?php the_title() ?></h1>
         <div class='post-content'><?php the_content() ?></div>
      
<?php
      endwhile;
   else : 
?>

      Oops, there are no posts.

<?php
   endif;
?>

The Same Loop Using WP_Query

<?php

   $args = array('cat' => 4);
   $category_posts = new WP_Query($args);

   if($category_posts->have_posts()) : 
      while($category_posts->have_posts()) : 
         $category_posts->the_post();
?>

         <h1><?php the_title() ?></h1>
         <div class='post-content'><?php the_content() ?></div>      
      
<?php
      endwhile;
   else: 
?>

      Oops, there are no posts.

<?php
   endif;
?>

As you can see, there is not much difference at all! Let’s break it down:

  1. Constructing a query
    On a category page, WordPress already knows that you want to list posts from that category. Because we are constructing a query from scratch using WP_Query, we need to specify this ourselves. We’ll delve a bit deeper into this in a bit.
  2. Instantiating the class and querying for posts
    By instantiating a class with the constructed argument array, WP_Query will try to pull the posts specified and a load of other details.
  3. Creating a loop
    You can use all of the usual functions; just be sure to use them as the methods of your object:
    • Instead of have_posts(), use $category_posts->have_posts().
    • Instead of the_post(), use $category_posts->the_post().
  4. Resume business as usual
    Once you’ve done the above, you can use all of the template tags you’ve come to know and love.

If you look at this in detail, you will find that the global $post object is also available. This means that if you use a custom loop like this within another loop, things can go wrong. Be sure to store the original value of the $post object and restore it after the loop.

<?php
   $temp_post = $post; // Storing the object temporarily
   $my_query = new WP_Query();
   while($my_query->have_posts()) {
      // Loop in here
   }
   $post = $temp_post; // Restore the value of $post to the original
?>

Digging Deeper

The Power of a Good Argument

The ease with which we can create loops is obvious, but what about actually querying for posts? Let me show you a common technique I use when creating sliders for commercial themes.

In many cases, users of your theme will want a great-looking slider, but they might be a bit lazy in creating content. Many users will also want to show future content. Let’s query for upcoming (i.e. unpublished) posts that have an attached featured image.

<?php
   $args = array(
      'post_status' => 'future',
      'meta_query' => array(
         array(
            'key' => '_thumbnail_id',
            'value' => '',
            'compare' => '!='
         )
      )
   );
   $slider_posts = new WP_Query($args);
?>

<?php if($slider_posts->have_posts()) : ?>

<div class='slider'>
   <?php while($slider_posts->have_posts()) : $slider_posts->the_post() ?>
      <div class='slide'>
         <?php the_post_thumbnail() ?>
      </div>
   <?php endwhile ?>
</div>

<?php endif ?>

Short, sweet and utterly understandable — just beautiful. And we’ve just scraped the surface.

Know Your Defaults

You may have noticed that I didn’t specify a number of things in my queries. What about how many posts to list? What about the post’s status in the first query we made?

Default values are supplied for many of the most common arguments. Here are a few that you don’t have to specify, unless you want to change them:

  • posts_per_page
    Defaults to the value specified in the reading settings for the number of posts to list.
  • post_type
    Defaults to post.
  • post_status
    Defaults to publish.

You can find the complete list of parameters in the Codex2, of course!

Arrays Are Awesome

In many cases, you will want to specify a number of values that an argument can take. Where it would seem logical, WP_Query usually allows you to use arrays to make your life easier. Here are a few examples:

  • You can use an array for post_status to pull posts from a number of different statuses. Note that you can use the string any to get posts from all statuses.
  • If you use custom post types, you’ll be happy to hear that you can use an array for the value of the post_type parameter as well.
  • For the taxonomy type parameters category__in, tag__in and so on, you can use an array to indicate a multitude of values.

Handling Taxonomies

WP_Query is nice enough to offer a simple way to make advanced taxonomy queries as well. This is especially useful for websites with complex set-ups and for commercial themes with large feature sets. The mechanism used is called tax_query. Let’s look at an example.

Say you have a website all about movies. You store movies in a custom “movie” post type; you have a custom taxonomy for genre, a custom taxonomy for actors, and you use the regular ol’ category to indicate how good a movie is. Let’s find all “Action” movies starring “Bruce Willis” that aren’t “Bad”:

<?php
   $args = array(
      'post_type' => 'movie',
      'tax_query' => array(
         'relation' => 'AND',
         array(
            'taxonomy' => 'category',
            'field' => 'slug',
            'terms' => array('bad')
            'operator' => 'NOT IN'
         ),
         array(
            'taxonomy' => 'genre',
            'field' => 'slug',
            'terms' => array('action')
         ),
         array(
            'taxonomy' => 'actor',
            'field' => 'slug',
            'terms' => array('Bruce Willis'),
         )
      )
   );
?>

While this hardcoded example would be useful only to people who love Die Hard, it’s not hard to see how an advanced filter can be built that lets users filter through your content in any which way they want.

Learn more about all of the awesome things you can do with the taxonomy parameters in the Codex3.

Having Fun With Meta Data

You’ve already seen that WP_Query is great at handling meta data — we used a meta_query in the second example to build a slider from posts that have featured images. Just as with the taxonomy queries, a lot of flexibility is built in here.

We’re currently building a WordPress theme to be used to create a Web page for apartment(s) for rent. We store apartments as a custom post type and use meta data to store the details. With a meta query, we can easily pull in all apartments that can house four or more people, that have a balcony and that are non-smoking.

<?php
   $args = array(
      'post_type' => 'apartment',
       'meta_query' => array(
         'relation' => 'AND',
          array(
             'key' => 'persons',
             'value' => '4',
             'compare' => '>=',
             'type' => 'NUMERIC'
          ),
          array(
             'key' => 'balcony',
             'value' => '1',
             'type' => 'BINARY',
             'compare' => '='
          ),
          array(
             'key' => 'smoking',
             'value' => '0',
             'type' => 'BINARY',
             'compare' => '='
          )
       )
   );
?>

Again, this is very modular, very understandable. To learn more about the parameters you can use, just visit the “Custom Field Parameters4” section in the WP_Query documentation.

Methods And Properties

Once you’ve made a query, you can coax a lot of information out of your object. You can find a full list of “Methods and Properties5” in the Codex. Here are the ones I tend to use most:

  • $query
    Shows you the query string passed to the $wp_query object. This is quite helpful for troubleshooting in some advanced cases.
  • $query_vars
    Shows an associative array of the arguments you’ve passed. If you do plenty of mixing and matching before passing arguments, this tool could be helpful indeed to check that all is well.
  • $posts
    Holds the requested posts from the database. I rarely use this property, but it’s good to know that this is where your items come from.
  • $found_posts
    A handy little thing that shows the total number of found items (without the limit imposed by the posts_per_page argument).

With Great Power Comes Great Responsibility

While WP_Query gives you plenty to play around with, it does have its drawbacks. Many people (my past self included) go nuts when they realize how easy it is to bang out queries all over the place.

Keep in mind that more queries mean more server load. I’ve found that on hosted systems, complex queries can be especially naughty because they eat at your RAM, which is probably your scarcest resource.

Make sure to check out what the default query holds on each page. What’s the sense in creating a new query for the latest posts on the front page if it’s there by default? Use what you can more than once, cache results, and so on.

(al)

Footnotes

  1. 1 http://www.smashingmagazine.com/?attachment_id=108029
  2. 2 http://codex.wordpress.org/Class_Reference/WP_Query#Parameters
  3. 3 http://codex.wordpress.org/Class_Reference/WP_Query#Taxonomy_Parameters
  4. 4 http://codex.wordpress.org/Class_Reference/WP_Query#Custom_Field_Parameters
  5. 5 http://codex.wordpress.org/Class_Reference/WP_Query#Methods_and_Properties

↑ Back to topShare on Twitter

Daniel is not only a web developer who is in love with WordPress, but also the Editor of the Smashing WordPress section. He owns a company called Bonsai Shed where they make web apps and premium themes like Musico and Estatement.

Advertising
  1. 1

    More informations for Default Loop, Loop with query_posts(), Loop with WP_Query(), Loop with get_posts() on http://digwp.com/2011/05/loops/ :)

    4
    • 2

      Konstantin Kovshenin

      January 15, 2013 10:58 am

      Yeah, except that you shouldn’t use query_posts because it’s evil.

      5
      • 3

        It has its place. It is ok for a low volume website and a quick fix, but it is definitely duplicating queries. If someone is interested in doing it a bit better, pre_get_posts is the hook to look for I think.

        -3
  2. 4

    Hi,
    If you call the_post() on on a custom query, be sure to call wp_reset_postdata() after the endwhile for not editing the main query.

    28
    • 5

      Konstantin Kovshenin

      January 15, 2013 10:56 am

      What Raherian said, use wp_reset_postdata to restore the post global from the original query. Also, “Bruce Willis” is probably not a valid term slug, though “bruce-willis” is. Great post, keep it up!

      -2
    • 7

      I second this, especially if you want to do more than one query per page/template.

      -1
  3. 8

    If you want to do it really good, look at this presentation by Andrew Nacin, one of WordPress’ core developers.
    http://www.slideshare.net/andrewnacin/you-dont-know-query-wordcamp-netherlands-2012

    4
  4. 10

    Great explanation and examples, except I’d always use taxonomies when many posts would have common values like smoking/non-smoking and custom fields when values will be unique to the post.

    0
    • 11

      Konstantin Kovshenin

      January 15, 2013 11:05 am

      I think taxonomies are faster to query, because you won’t have to CAST the meta value and thus perform a full table scan. Also, wp_term_relationships has an index on ( `object_id`,`term_taxonomy_id` ) so it has to be much faster than meta anyway.

      2
      • 12

        That is a very good point. It should still make sense semantically, but I agree, taxonomies should be much faster. Especially if you’re looking at a large dataset.

        0
    • 13

      This seems like a poor example to me, as by default WordPress’s taxonomies can’t be register to be “exclusive” you would be able to select both smoking & non-smoking for a single post, and in this example the value would seem to suit a Boolean’s behavior. You can of course either deregister the standard taxonomies meta box and register a replacement one rendering radio inputs instead of checkboxs, or just replace the content of the existing meta box via JavaScript. But in this example I can’t see how it would be justified. You’re not going to be adding any additional terms to a “smoking” taxonomy. It’s either going to be a true (smoking) or a false (non-smoking), Custom fields are definitely a more suitable solution to this scenario.

      5
      • 14

        Konstantin Kovshenin

        January 15, 2013 11:10 am

        Post meta is not different in that respect. You can have two meta fields with the same key, one saying smoking, and the other one saying non-smoking. It’s up to the developer to create a good interface, and handle the proper use cases during submission when posts are saved.

        1
      • 15

        I agree and disagree at the same time :) It definitely depends on the scenario! In the specific example, I was thinking about an apartment management system. An apartment can be:
        - smoking/non smooking
        - Pets allowed
        - Close to center

        And so on. Taxonomies in this sense are always boolean.

        If you want to add the distance of an apartment from the town center, that’s when I would use custom fields. The reason I would use custom taxonomies in many cases is that there is less hassle with the programming, you don’t need to create the controls manually.

        1
        • 16

          I see your points, it’s is indeed true that you don’t have to create the controls when using a taxonomy, but at the same time, the controls that are generated for you are not ideal, I would even say not fit for purpose*, so I’m not sure about the weight of this point.

          Following that, because the controls are not ideal, additional work will be required else where, the template and query would need further checks to identify and appropriately handle the scenario of multiple terms being assigned to the post, else you may end up with a property being listed as a result for both smoking and non-smoking queries, and also potentially displaying both terms in the listing it’s self also.

          There is also the issue of unnecessary functionality, by using a taxonomy for each of these you’d be adding three additional sub menus to the post type for term management, which in a boolean scenario, simply isn’t required. This isn’t a big deal if your running the site your self, but if you’re selling it, I’d definitely want to avoid this, more menus, more questions, more problems*.

          If this were a site I would be running my self, or for some one I know to be quite competent, I may cut a few corners* and use taxonomies, but for a proper client, I’d personally go the post meta route, a couple of radio buttons isn’t to hard to code up :)

          I realise this has left the focus of the article some what, so I’ll leave it here. :)

          Thanks again!

          * My opinion only :)

          5
          • 17

            I completely agree with this part of your comment. I agree that if you’re working for a client you shouldn’t cut corners like this, but for personal work or for smaller jobs I think it’s fine.

            Coding with WordPress for high volume / high profile clients is usually a completely different thing, perhaps it would merit an article?…

            1
  5. 18

    I thought the whole concept was simple with word press, didn’t know we have this much complex coding in between. This is a lot of information.

    1
    • 19

      Complex functionality requires more complex code. It is still pretty simple and clear. It is a programmers job to make it simple and seamless for the user and WordPress gives you pretty good tools to do it.

      2
  6. 20

    Regarding the paragraph “The same loop using wp_query”, I would to say that, when you starts a custom loop using the WP_Query class, there is no need to temporarily store the $post variable into another variable. In fact, at the end of the custom loop, you can easily restore the content of the $post of the main loop using wp_reset_postdata(). In other words, there is no need of this:

    $temp_post = $post;

    $post = $temp_post;

    because the content of the $post of the main loop is not forgotten… unless you need to recall something of the original $post variable into the custom loop.

    Also, take note that:
    a) to reset the $post populated by WP_Query you should use wp_reset_postdata();
    b) to reset the $post populated by query_posts you should use wp_reset_query().

    5
  7. 21

    You should remove the part about resuming business and using a temporary variable to store $post, and replace it with a paragraph on wp_reset_postdata();

    Every moment it’s up there is another question from a confused rookie dev on stack exchange.

    Also you should modify your code examples to use consistent coding style, some use shorthand others use the braces style.

    5
  8. 22

    Really informative article for wp newbi .

    I am still sucked in to fetch information form metadata, where data stored in serialized format.
    Let me know any one have idea in this.

    1
  9. 23

    I’m not one to compare cms’s all the time, but this sounds like the beginning of WordPress’ own views module. It could do with an interface too.

    1
  10. 24

    yeah , its really nice narration about WP_Query function. Also love the way the explanation of this particular author ;)

    Thanks!

    1
  11. 25

    Is there any method to sort posts by discount value between two different meta keys?
    Example:
    meta_key = price
    meta_key = lowprice
    discount = (price – lowprice) / price * 100)
    I want to order posts by this result. :)

    3
  12. 26

    Amazing post!!
    One thing, In the example about apartment(s), is there a way of making the query dynamic?
    I want to use a couple of dropdowns and a “filter” button. So I can choose ’2 or more bedrooms’ + ’1 or more bathroom’, etc.
    Can somebody put me in the right path? (I have almost no knowledge of php)

    Thanks

    0
  13. 27

    hi thanks for this post i did learn alot from your articale.

    taxonomys are great way to creat cutom query and search in parameters and i use them a lot.
    i am trynig to combain few parameters in the query
    1. from metadata meta_key meta_value… as you mantion in the apartments
    &
    2. from the taxonomy

    etc are ok but when combination creat i get php synt.. error,
    prheps you could show and exemple how to JOIN in diffrent tables…

    thanks in advance

    0
  14. 28

    Great post!

    How can we add more arguments to an existing query? Suppose I have

    if (!$blog_query) $blog_query = $wp_query;

    while( $blog_query->have_posts() ) : $blog_query->the_post();

    AND I want to add to the existing qurey two arguments before passing, say

    ‘orderby’ => ‘post-title’,
    ‘order’ => ‘ASC’,

    How can I ad these to the existing $blog_query ??

    0
  15. 29

    I tryed the “wp query” for post-type that I created, but the function don’t agree, any solution?
    i attache my script
    ‘spettacoli’,
    ‘tag’=>’12′); ?>

    <a href="” title=””>

    150) {
    $text = substr($text , 0, 150);
    }
    echo $text.’ [...]‘;
    ?>

    this should show me all my post-type “spettacoli” on a page or anywhere other place, right?

    0
  16. 31

    I love you!!! This post saved my life! :D

    THANK YOU! :D

    0
  17. 32

    Can you edit the query with the corrections, suggested in the comments, i.e., wp_reset_postdata()? I enjoy reading this article of yours as you describe the custom loops I’d like use in my n00b theme

    0
  18. 33

    I want to list 5 posts from each category that has more than 5 posts on my index page. Do I have to put the code again and again or there can be a loop to run WP_Query for every category? Also, is there any other better way of dong that?

    0
  19. 34

    Great post.
    One more thing I want to know, what about combining of meta_query and taxonomy_query together in a single wp_query.

    Will it work properly?

    0
  20. 35

    Hi,

    Thanks for the post it is helpfull, after read the meta_query secction I wander if it is flexible enough to perform something like this:

    ‘meta_query’ => array(
    ‘relation’ => ‘AND’,
    array(
    ‘key’ => ‘Meta_geo’,
    ‘value’ => ’46′,
    ‘compare’ => ‘=’,
    ),
    array(
    ‘key’ => ‘Meta_dest’,
    ‘value’ => ‘si’,
    ‘compare’ => ‘=’,
    ),
    array(
    ‘relation’ => ‘OR’,

    array(
    array(
    ‘key’ => ‘Meta_1′,
    ‘value’ => ’10′,
    ‘compare’ => ‘ ‘Meta_1′,
    ‘value’ => ’30′,
    ‘compare’ => ‘>=’,
    )
    ),

    array(
    ‘relation’ => ‘OR’,
    array(
    ‘key’ => ‘Meta_1′,
    ‘value’ => ”,
    ‘compare’ => ‘=’,
    ),
    array(
    ‘key’ => ‘Meta_1′,
    ‘value’ => ”,
    ‘compare’ => ‘=’,
    )
    )
    )
    ),

    if not I dont know how can i build complex querys in wordpress.. thanks.

    0

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

↑ Back to top