Crash course in WP_Query

WP_Query is a class provided by WordPress which we can use to create custom queries. Example of custom queries are:

  • Getting related posts
  • Showing posts of different post_type
  • Showing posts of multiple categories
  • Etc. Etc. we will talk about more use cases.

We will discuss more about the use cases of WP_Query but first let's talk about different terms and definitions. We are going to learn about the following terms:

The Loop

It is a set of php code to display posts.

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>  
  //Loop
<?php endwhile; else : ?>  
    <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
<?php endif; ?>  

if(have_posts()) It checks if there is any posts available or not.

while ( have_posts() ) As long as have_posts() returns true value, the while loop will keep looping.

The function the_post() takes the current item in the collection of posts and makes it available for use inside this iteration of The Loop. Without it many of the template tags like the_permalink() the_content() the_titte() will not work.

$wp_query

It holds the data for the current query. For example print_r($wp_query->query) will print the query string of current query.

$post

It contains the data of current post and is a global variable.

<?php  
global $post;  
$post -> ID;
//(integer) The post ID.
$post -> post_author;
//(integer) The ID of the post author.
$post -> post_date;
//(string) The post date using the server's current timezone. Format: yyyy-mm-dd hh:mm:ss Example: 2011-05-19 13:51:21 (default Unix format 'F j, Y')
$post -> post_date_gmt;
//(string) Same as above, but in GMT.
$post -> post_content;
//(string) The post content.
$post -> post_title;
//(string) The post's title.
$post -> post_excerpt;
//(string) The post's excerpt, if one is set.
$post -> post_status;
//(string) See get_post_status for possible values.
$post -> comment_status;
//(string) See wp_get_comment_status for possible values.
$post -> ping_status;
//(string) Values: 'open' or 'closed'.
$post -> post_password;
//(string) The post's password.
$post -> post_name;
//(string) The post's slug.
$post -> to_ping;
//(string) Addresses to be pinged.
$post -> pinged;
//(string) Addresses already pinged.
$post -> post_modified;
//(string) Date the post was last modified using server's timezone.
$post -> post_modified_gmt;
//(string) Same as above in GMT.
post_content_filtered  
$post -> post_parent;
//(integer) ID of the post's parent, if it has one.
$post -> guid;
//(string) A link to the post. This is not the permalink. To get the permalink use get_permalink.
$post -> menu_order;
//(integer) The value set in the 'Order' field for pages.
$post -> post_type;
//(string) The post type.
$post -> post_mime_type;
//(string) Mime Type (for attachments, etc).
$post -> comment_count;
//(integer) The number of approved comments.
$post -> ancestors;
//(array) An array of the post's ancestors' IDs (parent, parent's parent, etc). [0] = parent, [1] = grandparent, etc.
?>

Post Meta

These are the custom fields saved for a post in the _postmeta database table.

Post Types

A post can be of different post type, by default there are five post types in WordPress which are post, page, attachment, revision, nav_menu_item.

We can also register a custom post type by using register_post_type() function.

Here's an example of registering custom post type 'recipes'

add_action( 'init', 'create_post_type' );  
function create_post_type() {  
  register_post_type( 'recipe',
    array(
      'labels' => array(
        'name' => __( 'Recipes' ),
        'singular_name' => __( 'Recipe' )
      ),
      'public' => true,
      'has_archive' => true,
    )
  );
}

Taxonomy and Terms

Taxonomies are used to group posts together. Then name for the different grouping in a taxonomy are called terms, for example in case of recipes italian or indian are terms of the taxonomy cuisine(custom taxonomy).

A taxonomy can be hierarchical or not hierarchical. By default WordPress provides category taxonomy which is hierarchical while the other taxonomies like posttag and postformat are non hierarchical. You can also register custom taxonomy using register_taxonomy() function.

function cuisine_init() {  
    // create a new taxonomy
    register_taxonomy(
        'cuisine',
        'recipe',
        array(
            'label' => __( 'Cuisine' ),
            'rewrite' => array( 'slug' => 'cuisine' )
        )
    );

register_taxonomy(  
        'recipe_type',
        'recipe',
        array(
            'label' => __( 'Recipe Type' ),
            'rewrite' => array( 'slug' => 'recipe-type' )
        )
    );

}
add_action( 'init', 'cuisine_init' );  

In the above function we are defining two taxonomies:

cuisine taxonomy for post_type recipe and the rewrite slug is defined to make the url /cuisine/.

recipe_type taxonomy for post_type 'recipe', we can use this taxonomy to group the recipes according to type like bread, snacks, salad, curry etc. etc.

As we have covered some basic terms of WordPress let's start talking about WP_Query now.

WP_Query - The Boss

As we know that we can use WP_Query to create custom queries/loops and show records accordingly in our templates.

Let's build some custom loops.

Get posts of post_type recipe

$paged = get_query_var( 'paged', 1 );
$args = array(
  'post_type' => 'recipe',
  'post_status' => 'publish',
  'paged' => $paged
);
$the_query = WP_Query($args);
// The Loop
if ( $the_query->have_posts() ) {  
    while ( $the_query->have_posts() ) {
        $the_query->the_post();
        //Do The Loop   
    }
} else {
    // no posts found
}

In the above function we fetching post of post type recipe by assigning value 'recipe' to 'post_type' key.

get_query_var retrieves public query variables that are recognized by WPQuery. This means if you are creating your own custom query vars that you append to URLs(?myvar=foo) then you have to add them to the public query variables by adding a filter.

function add_query_vars_filter( $vars ){  
  $vars[] = "my_var";
  return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );  

Get posts where cuisine is italian or indian.

$args = array(
    'post_type' => 'recipe',
    'tax_query' => array(
        array(
            'taxonomy' => 'cuisine',
            'field'    => 'slug',
            'terms'    => array('italian', 'indian'),
        ),
    ),
);
$query = new WP_Query( $args );

In the above function we are passing an array to key tax_query. This construct allows you query multiple taxonomies by using relation parameter.

So let's get all the recipes of cuisine italian or indian and of recipe_type bread or salad.

$args = array(
    'post_type' => 'recipe',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'cuisine',
            'field'    => 'slug',
            'terms'    => array( 'italian', 'indian' ),
        ),
        array(
            'taxonomy' => 'recipe_type',
            'field'    => 'slug',
            'terms'    => array( 'bread', 'salad')
        ),
    ),
);
$query = new WP_Query( $args );

Following are the parameters which you can pass in tax_query

  • tax_query
    • relation - Logical relation between each inner taxonomy array. Possible values are 'AND' or 'OR'
    • taxonomy array. The values which you can pass are as follows
      • taxonomy - Name of the taxonomy eg. cuisine
      • field - Select taxonomy term by term_id, name or slug
      • terms - Taxonomy terms
      • include_children - Default is true and it will include all the children of hierarchical taxonomy
      • operator - This will test the multiple terms by 'IN', 'NOT IN', 'AND', 'EXISTS' and 'NOT EXISTS'. Default value is 'IN'.

Get recipes by meta values.

Suppose there is post meta field of name 'prep_time' and with every recipe post we have saved the time duration of the preparation of the recipe in custom meta field 'prep_time' in minutes.

So let's get all the recipes which will take less than 5 minutes to prepare

$args = array(
    'post_type'  => 'recipe',
    'meta_query' => array(
        array(
            'key'     => 'prep_time',
            'value'   => 5,
            'compare' => '<=',
        ),
    ),
);
$query = new WP_Query( $args );

Let's take couple of more custom post meta into consideration. We are going to add cooking_time and editor_choice.

cooking_time: How long one have to cook for recipe to be completed.
editor_choice: [True or false] Self explanatory. :)

Let's get all the recipes which can be prepared less than equal to 5 minutes, cooking time should be less than 45 minutes and recipe should be editor's choice.

$args = array(
    'post_type'  => 'recipe',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'     => 'prep_time',
            'value'   => 5,
            'compare' => '<=',
        ),
        array(
            'key'     => 'cooking_time',
            'value'   => 45,
            'compare' => '<=',
        ),
        array(
            'key'     => 'editor_choice',
            'value'   => 'true',
            'compare' => '=',
        )
    )
);
$query = new WP_Query( $args );

Let's get all the recipes which are either editor's choice or the one's whose cooking_time is less than 45 mins and prep_time is less than 5 mins.

$args = array(
    'post_type' => 'recipe',
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'editor_choice',
            'value' => 'true',
            'compare' => '=',
        ),
        array(
            'relation' => 'AND',
            array(
                'key' => 'prep_time',
                'value' => 5,
                'compare' => '<=',
            ),
            array(
                'key' => 'cooking_time',
                'value' => 45,
                'compare' => '<=',
            )
        )
    )
);
$query = new WP_Query($args);

We can mix this with different filters like:

Getting recipes associated with certain author(s)

author (int - author id) - array('author' => 1).
author_name (string) - use 'user_nicename' - array('author_name' => 'sunny').
author__in (array) - All recipes from a set of authors array('author__in' => array(1,4,5)) .
author__not_in (array) - Exclude recipes from a set of authors array('author__not_in' => array(1,4,5)).

Mix taxonomies and custom meta

Add more filter and bring in taxonomies. Let's get all the recipes of cuisine indian or italian of type bread or salad and is either editor's choice or the one's whose cooking_time is less than 45 mins and prep_time is less than 5 mins.

$args = array(
    'post_type' => 'recipe',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'cuisine',
            'field'    => 'slug',
            'terms'    => array( 'italian', 'indian' ),
        ),
        array(
            'taxonomy' => 'recipe_type',
            'field'    => 'slug',
            'terms'    => array( 'bread', 'salad')
        ),
    ),
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'editor_choice',
            'value' => 'true',
            'compare' => '=',
        ),
        array(
            'relation' => 'AND',
            array(
                'key' => 'prep_time',
                'value' => 5,
                'compare' => '<=',
            ),
            array(
                'key' => 'cooking_time',
                'value' => 45,
                'compare' => '<=',
            )
        )
    )
);
$query = new WP_Query($args);

That's it guys, will keep updating this post with more examples in coming weeks. Till then peace.

comments powered by Disqus