WordPress at a glance

WP_Query{} WP 1.5.0

The WordPress Query class. Selects posts from the database based on the specified parameters. get_posts(), query_posts() and all other query functions for posts selection from wp_posts table are based on this class.

Filter notes

You can use the following filters to embed directly into the SQL query itself.

Important: if parameter suppress_filters is specified, then all hooks mentioned below will not work!

Filters triggered before the pagination request is set:
Filters triggered after the pagination request is set:
  • posts_where_paged - modify the SQL 'WHERE' during pagination
  • posts_groupby - changes SQL 'GROUP BY'
  • posts_join_paged - changes SQL 'JOIN' during pagination
  • posts_orderby - changes SQL 'ORDER BY'
  • posts_distinct - changes SQL 'DISTINCTROW'
  • post_limits - changes SQL 'LIMIT'
  • posts_fields - changes the retrieving table fields
  • posts_clauses - allows to change all parts at the same time, passes an array:

    Array (
    	[where]    =>  AND 1 = 1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
    	[groupby]  => wp_posts.ID
    	[join]     => INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )
    	[orderby]  => wp_posts.post_date DESC
    	[distinct] => 
    	[fields]   => wp_posts.*
    	[limits]   => LIMIT 0, 10
    )
Is the basis for: get_posts(), query_posts()
Return

WP_Query object. Object property ->posts contains array of posts (WP_Post objects).

menu

Introduction to WP_Query

WP_Query is a PHP class that allows you to get posts from the database using a variety of criteria. For example, we can get posts:

  • for a certain period of time.
  • from the specified category, tag.
  • last posts, random posts, popular posts.
  • posts with specified custom fields or a set of such fields.

Consider a simple query:

$query = new WP_Query( array( 'category_name' => 'news' ) );

So, WordPress will make a request to the database to get posts from the category "news".

Now, the $query variable contains an object with the results of the query. Let's process the result with the help of special methods:

while ( $query->have_posts() ) {
	$query->the_post();

	the_title(); // print the post title
}

Using have_posts() we check if there are any posts in $query object. the_post() prepares the current post in the loop for output, making available the use of template tags (functions) like the_title(), the_content() and so on.

WP_Query is constantly used in WordPress. For example, during the generation of any page, WP_Query creates a global variable $wp_query which stores the information about the current request. Based on this information, WordPress determines which page we are currently on (post, archive, tag, etc.).

Also, when building The Loop, data from $wp_query is used by different template tags, like the_permalink(), the_content().

And also, $wp_query stores other data; and the data is different on different types of pages. You can see what data is stored in $wp_query as follows:

global $wp_query;
print_r( $wp_query );

Often you get posts data without working directly with this class and global variables. This is because you have functions: conditional tags, template tags. All such functions need to be used inside The Loop.

Almost all conditional tags use this class. Also, when creating The Loop: if( have_posts() )..., all data output functions work with this class. Thus, WP_Query is used for every WordPress page.

If you use the_post() in the loop, you should also use wp_reset_postdata() function after the loop (see the diagram below).

The Loop code can look like this (it's the same as with get_posts(), but without predefined parameters):

// create an instance
$my_posts = new WP_Query;

// do the query
$myposts = $my_posts->query( array(
	'post_type' => 'page'
) );

// process the result
foreach( $myposts as $pst ){
	echo esc_html( $pst->post_title );
}

For a better understanding of WP_Query and different types of loops, look at this diagram:

How WordPress query functions work

WP_Query usage templates

#1 Standard Loop

// set the necessary criteria for selecting data from the database
$args = array(
	'posts_per_page' => 5,
	'orderby'        => 'comment_count'
);

$query = new WP_Query( $args );

// loop
if ( $query->have_posts() ) {
	while ( $query->have_posts() ) {
		$query->the_post();
		echo '<li>' . get_the_title() . '</li>';
	}
} else {
	// no posts found
}
// Return back the original post data. Reset $post.
wp_reset_postdata();

#2 Standard Loop (Alternate)

<?php 

// the query
$the_query = new WP_Query( $args ); ?>

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

	<!-- pagination here -->

	<!-- the loop -->
	<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<h2><?php the_title(); ?></h2>
	<?php endwhile; ?>
	<!-- end of the loop -->

	<!-- pagination here -->

	<?php wp_reset_postdata(); ?>

<?php else : ?>
	<p><?php esc_html_e( 'Sorry, no posts matched your criteria.' ); ?></p>
<?php endif; ?>

#3 Multiple Loops

// query to DB
$the_query = new WP_Query( $args );

// loop
while ( $the_query->have_posts() ) {
	$the_query->the_post();
	echo '<li>' . get_the_title() . '</li>';
}

/* 
Restore the original data.
We use new WP_Query, this means we have no impact on the global
variable '$wp_query', so there is no need to reset the query and 
only post data can be reset.
*/
wp_reset_postdata();

// Second query (without global variable)
$query2 = new WP_Query( $args2 );

// 2nd loop
while( $query2->have_posts() ) {
	$query2->next_post();
	echo '<li>' . get_the_title( $query2->post->ID ) . '</li>';
}

// Restore original post data
wp_reset_postdata();

#4 get_posts() analogue

$args = array(
	'post_type' => 'page'
);
$query = new WP_Query;
$my_posts = $query->query( $args );

foreach( $my_posts as $my_post ){
	echo '<p>'. $my_post->post_title .'</p>';
}

When creating queries, WP_Query may have predefined parameters that we need keep in mind. For example, $query = new WP_Query( 'post_type=func' ); may return only the first 10 posts, not all of them, as you might expected. Because the preset parameter posts_per_page=10 limits the number of posts to 10. Here is the list of parameters that may be predefined and interfere with your query: post_type, posts_per_page, orderby, order, post_status, offset.

See below the list of all possible parameters or in the code of WP_Query::parse_query() method.

menu

Category parameters

Gets posts from specified categories.

cat(int/string/array)
The ID of the category. You can specify multiple IDs in a comma-separated string or in an array. To exclude category, specify the minus sign (-) before the ID of the category (it also exclude all sub-categories). To skip excluding sub-categories, use the category__not_in.
category_name(string)
Category name. Use a slug (alternative name) rather than the category name itself.
category__and(array)
Get posts that fall into multiple categories at the same time.
category__in(array)
Get posts that fall into one of these categories.
category__not_in(array)

Get posts that are not in the specified categories.

This parameter does not consider the nested categories. To exclude a category and nested categories, specify the category ID separated by a comma with a minus sign in cat parameter: cat=-25,-32.

Posts from one category

Get posts from category with id 4 (posts from child categories will also be selected):

$query = new WP_Query('cat=4');

Get posts from one category by a slug (alternative name of the category):

$query = new WP_Query('category_name=staff');

Get posts only from category 4 (child categories are not affected):

$query = new WP_Query( 'category__in=4' );

Posts from several categories

Output posts from categories by categories IDs:

$query = new WP_Query('cat=2,6,17,38');

Get posts from categories by their slug:

$query = new WP_Query( 'category_name=staff,news' );

Exclude posts belonging to categories

Let's output all posts, except the ones with categories 12,34,56:

$query = new WP_Query( 'cat=-12,-34,-56' );

Show all posts except those that belong to category 3:

$query = new WP_Query('cat=-3');

Posts from multiple categories simultaneously

Show posts that are in two categories at once:

$query = new WP_Query( array( 'category__and' => array(2,6) ) );

Show posts that are in at least one of the categories (child categories will not be considered):

$query = new WP_Query(array('category__in' => array(2,6)));

You can exclude several categories, like this:

$query = new WP_Query( array('category__not_in' => array(2,6)) );
menu

Tags parameters

Gets posts related to specific tags.

tag(string)
Tag slug.
tag_id(int)
Tag ID.
tag__and(array)
Posts with several tags. You must specify ID.
tag__in(array)
Posts with at least one of the specified tags. You must specify ID.
tag__not_in(array)
Posts not related to the specified tags. You must specify ID.
tag_slug__and(array)
Same as tag__and, but tag slug (alternative name) should be specified (instead of ID).
tag_slug__in(array)
Same as tag__in, but tag slug (alternative name) should be specified (instead of ID).

Posts with one tag

Get posts with the tag cooking. You must specify the tag slug, but not ID.

$query = new WP_Query('tag=cooking');

Get posts that have at least one specified tag:

$query = new WP_Query('tag=bread,baking');

Posts with multiple tags

Get posts that have one of these tags:

$query = new WP_Query( 'tag=bread,baking' );

Get posts having all specified tags at once:

$query = new WP_Query('tag=bread+baking+recipe');

Get posts with two tags (37 and 47). This option is more preferable to the above because of the performance.

$query = new WP_Query( array('tag__and' => array(37,47)) );

Get posts with at least one of these tags (37 or 47). This option is more preferable because you specify IDs (so code works more quickly).

$query = new WP_Query( array('tag__in' => array(37,47)) );

Get posts that don't have tags with ID 37 and 47:

$query = new WP_Query(array( 'tag__not_in'=>array(37,47) ));
menu

Taxonomy parameters

Gets posts related to a particular taxonomy.

{tax}(string)
Use taxonomy name and term slug: array('category'=>'wordpress'). Deprecated since version 3.1 in favor of 'tax_query'.
tax_query(array)

Since versions 3.1. You must specify the query parameters for the taxonomy. It can take a number of nested arguments, such as:

  • relation (string)
    How to select posts from specified taxonomies. Can be:

    • AND - posts that are simultaneously included in the specified taxonomies terms.
    • OR - posts belonging to any of the specified taxonomies terms.

    It is specified in the first array, and the terms queries specified in nested arrays, which can have the following parameters:

    • taxonomy (string)
      The name of the taxonomy.

    • field (string)
      The field that will be specified in the terms parameter. Can be:

      • id or term_id - specify term id in terms parameter.
      • name - specify term title (name) in terms parameter.
      • slug - specify term slug (alternative name) in terms parameter.

        Default: 'term_id'

    • terms (int/string/array)
      The terms of the taxonomy from which you want to get the posts.

    • operator (string)
      An operator that specifies how to compare the specified terms in the terms parameter. Can be:

      • IN - posts from the specified terms (default).
      • NOT IN - posts from all terms except specified.
      • AND - posts simultaneously belonging to all specified terms.
      • EXISTS - posts from taxonomy (any term exists). In this case, you do not need to specify the terms parameter. Since version 4.1.
      • NOT EXISTS - posts not in taxonomy (no one term exists). In this case, you do not need to specify the terms parameter. Since version 4.1.

        Default: 'IN'

    • include_children (true/false)
      Include or not posts from child terms (for tree-like taxonomies). true - enable (include).
      Default: true

tax_query is an array with array elements, each of nested arrays specifies taxonomy query, its terms and how to use these terms in the query. The relation operator is specified in the first tax_query array and determines how to compare nested arrays. This design allows you to query multiple taxonomies at the same time.

#1 Retrieve posts from a single taxonomy

Get posts from the term bob, which is an element of people taxonomy:

$query = new WP_Query( [ 'people' => 'bob' ] );

#2 The same but with post type book_author

Get posts with book_author post type with the term bob, which is an element of people taxonomy:

$query = new WP_Query( [
	'post_type' => 'book_author',
	'people'    => 'bob'
] );

Let's do the same as in example above, but using the tax_query:

$query = new WP_Query( [
	'tax_query' => [
		[
			'taxonomy' => 'people',
			'field'    => 'slug',
			'terms'    => 'bob'
		]
	]
] );

#3 Output from multiple taxonomies

Get posts from custom taxonomies: people and language.

$query = new WP_Query( array(
	'post_type' => 'post',
	'people'    => 'bob',
	'language'  => 'english'
) );

Same as in the example above. Let's output the posts that also belong to several custom taxonomies using tax_query:

$query = new WP_Query( [
	'tax_query' => [
		'relation' => 'AND',
		[
			'taxonomy' => 'movie_janner',
			'field'    => 'slug',
			'terms'    => array( 'action', 'comedy' ),
		],
		[
			'taxonomy' => 'actor',
			'field'    => 'id',
			'terms'    => array( 103, 115, 206 ),
			'operator' => 'NOT IN',
		]
	]
] );

#4 Using the argument relation=OR

Output posts that are in the quotes category or that have the quote format (formats introduced in version 3.1). Use the relation argument:

$query = new WP_Query( [
	'post_type' => 'post',
	'tax_query' => [
		'relation' => 'OR',
		[
			'taxonomy' => 'category',
			'field' => 'slug',
			'terms' => [ 'quotes' ]
		],
		[
			'taxonomy' => 'post_format',
			'field' => 'slug',
			'terms' => [ 'post-format-quote' ]
		]
	]
] );

#5 Multi-level complex query using operator relation=OR

Let's say we want to get posts of one of the groups of taxonomy elements.

For example, we need to get cars of ford brand with black color or cars of bmw brand with white color.

In this example the first relation=OR operator affects on first child nested arrays. And relation=AND operator in nested arrays affects on arrays of nested arrays.

$query = new WP_Query(
	[
		'tax_query' => [
			'relation' => 'OR',
			[
				'relation' => 'AND',
				[
					'taxonomy' => 'brand',
					'field'    => 'slug',
					'terms'    => [ 'ford' ]
				],
				[
					'taxonomy' => 'color',
					'field'    => 'slug',
					'terms'    => [ 'black' ]
				]
			],
			[
				'relation' => 'AND',
				[
					'taxonomy' => 'brand',
					'field'    => 'slug',
					'terms'    => [ 'bmw' ]
				],
				[
					'taxonomy' => 'color',
					'field'    => 'slug',
					'terms'    => [ 'white' ]

				]
			]
		]
	]
);

$query = new WP_Query( $args );

Usage of get_posts()

#6 Print titles of post type countries from all sub-terms of the term 62, from the country taxonomy:

$args = array(
	'tax_query' => array(
		array(
			'taxonomy' => 'country',
			'field'    => 'id',
			'terms'    => array( 62 )
		)
	),
	'post_type' => 'countries',
	'posts_per_page' => -1
);
$posts = get_posts( $args );

foreach( $posts as $pst ){
	echo $pst->post_title .'<br>';
}

#7 Output all posts of article custom post type not related to the specified terms ('term-slug',' term-slug-two',' term-slug-three') of taxonomy (artictag):

// get all posts not related to the specified terms
$post_type = 'article';
$tax       = 'artictag';
$terms     = array('term-slug', 'term-slug-two', 'term-slug-three');

$psts  = get_posts( array(
	'posts_per_page' => -1,
	'post_type' => $post_type,
	'tax_query' => array(
			'relation' => 'AND',
			array(
				'taxonomy' => $tax,
				'terms' => $terms,
				'field' => 'slug',
				'operator' => 'NOT IN',
			)
		),
	'' => '',
) );

$i = 0;
foreach( $psts as $pst ){
	echo ++$i . ' <a href="'. get_permalink( $pst->ID ) .'">'. $pst->post_title .'</a><br>';
}

#8 Get all posts of custom post type func that are not in taxonomy func_cat

I.e. get func posts, with no one term from func_cat taxonomy. For doing this, we use operator=NOT EXISTS parameter.

$posts = get_posts( [
	'tax_query' => [
		[
			'taxonomy' => 'func_cat',
			'operator' => 'NOT EXISTS',
		]
	],
	'post_type' => 'func',
	'posts_per_page' => -1
] );

foreach( $posts as $pst ){
	echo $pst->post_title .'<br>';
}

#9 Get posts from taxonomy and sort by metafield

Get posts of type 'func' that are placed in any element of taxonomy 'tplcat' and sort these posts by numeric metafield 'views_prev_month'.

$query = new WP_Query( [
	'post_type'      => 'func',
	'posts_per_page' => -1,
	'tax_query' => [
		[
			'taxonomy' => 'tplcat',
			'operator' => 'EXISTS',
		]
	],
	'meta_key' => 'views_prev_month',
	'orderby'  => [ 'meta_value_num'=>'DESC' ],
] );
menu

Author parameters

Gets the posts belonging to certain authors.

author(int)
Author ID.
author_name(string)
The nickname of the author. You must specify a field value user_nicename.
author__in(array)
An array of IDs of the authors whose posts you want to retrieve.
author__not_in(array)
An array of IDs of the authors whose posts you want to exclude.

#1 Get posts for a single author

Posts by author ID:

$query = new WP_Query( 'author=123' );

Posts by author name (nickname):

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

#2 Let's output posts of several authors at once

Posts of 4 authors by ID:

$query = new WP_Query( 'author=2,6,17,38' );

#3 Exclude posts by author

Get all posts except his posts except of author 12:

$query = new WP_Query( 'author=-12' );

#4 More example

Show all pages of author 1 (author=1), sort by title (orderby=title) in alphabetical order (order=ASC) and do not show sticky posts at the top.

$query = new WP_Query('caller_get_posts=1&author=1&post_type=page&post_status=publish&orderby=title&order=ASC');

#5 Several authors at the same time

Get all posts of authors with ID 2 and 6:

$query = new WP_Query( [ 'author__in' => [ 2, 6 ] ] );

Get posts of all authors, except authors with ID 2 and 6:

$query = new WP_Query( [ 'author__not_in' => [ 2, 6 ] ] );
menu

Posts (pages) parameters

Gets posts, pages and custom post types.

p(int)
ID of the post to be received. Ex: p=27.
name(string)
post_name field of post. Ex: name=about-site.
title(string)
Post title. Example: name=About Site. Since 4.4.
page_id(int)
ID of the static page you want to get. Ex: page_id=27.
pagename(string)
Name (post_name) of static page. Ex: pagename=about-site.
post_parent(int)
Returns child pages of specified post ID.
post_parent__in(string/array)
Select posts whose parents are specified in this array.
post_parent__not_in(string/array)
Select posts whose parents are not specified in this array.
post__in(string/array)

Specify the comma-separated ID of the posts you want to receive. Ex: post__in=5,12,2,14,7.

Note: if there are sticky posts, they will be included automatically. You can disable them by setting ignore_sticky_posts

post__not_in(string/array)
Displays all posts except specified.
post_name__in(string/array)
Select the specified posts. Specify post_name (slug) separated by commas or in an array. Since version 4.4.

Posts / pages by ID

Output post by ID:

$query = new WP_Query('p=7');

Output page by ID:

$query = new WP_Query( 'page_id=7' );

Posts / pages by post_name (slug)

Output post by post_name (slug):

$query = new WP_Query( 'name=about-my-life' );

Output page by post_name (slug):

$query = new WP_Query( 'pagename=contact' );

Child posts / pages

Get child page using parent and current post_names separated with a slash: E.g: parent_slug/child_slug.

$query = new WP_Query( 'pagename=contact_us/canada&post_type=page' );

Get the child pages using the parent's ID:

$query = new WP_Query( 'post_parent=93&post_type=page' );

Get top-level pages, except all child pages:

$query = new WP_Query( 'post_parent=0&post_type=page' );

Output the posts whose parents are specified in the array:

$query = new WP_Query( [ 'post_parent__in' => [ 2, 5, 12, 14, 20 ] ] );

Multiple outputing of post/pages

Output only the specified posts:

$query = new WP_Query( 
	[ 'post_type'=>'page', 'post__in' => [ 2, 5, 12, 14, 20 ] ] 
);

Output all posts except the specified ones:

$query = new WP_Query( [
	'post_type'    => 'post',
	'post__not_in' => [ 2, 5, 12, 14, 20 ]
] );

Note: you cannot combine post__in and post__not_in in one request.

Do not use a comma-separated ID string, this will not work. You need to pass a ready array:

// WRONG
$exclude_ids = '1,2,3';
$query = new WP_Query( [ 'post__not_in' => [ $exclude_ids ] ] );

// CORRECT
$exclude_ids = [ 1, 2, 3 ];
$query = new WP_Query( [ 'post__not_in' => $exclude_ids ] );
menu

Post Types parameters

Get posts of the specified type.

post_type(string/array)

Posts of what type should be retrieved. Default: post. But if specify tax_query parameter, the default will be any.

Can be:

  • any - include all types, except revision, and types with exclude_from_search=true parameter specified.
  • attachment - by default WP_Query sets the status 'post_status'=>'publish'. But attachments has status 'post_status'=>'inherit', so to display attachments you need also to change post_status parameter to inherit or any.
  • page
  • post
  • revision
  • custom_type - name (slug) of any custom post type.
  • [ 'post', 'page' ] - several types specified in array.

Default: post

Posts by type

Get only pages:

$query = new WP_Query( 'post_type=page' );

Fetch all posts, except revisions and post types with registration parameter exclude_from_search = true:

$query = new WP_Query( 'post_type=any' );

Get 4 types of posts at the same time:

$query = new WP_Query( [
	'post_type' => [ 'post', 'page', 'movie', 'book' ]
] );
menu

Custom fields (postmeta) parameters

The main parameter for working with metadata in WP_Query is meta_query. It gets posts by their custom fields keys and values. Principle of meta_query usage is the same as tax_query — array, in which each element is child array with query parameters, i.e. meta_query is an array of arrays.

Such array design allows multiple requests. The first parameter relation in the main array describes the logical connection between the requests. It can take the values AND (used by default. Match all queries) or OR (match any query).

$args = [
	'meta_query' => [
		'relation' => 'OR',
		[
			'key' => 'color',
			'value' => 'blue'
		],
		[
			'key' => 'price',
			'value' => 20
		]
	]
];

Old meta data query variables: meta_key, meta_value, meta_value_num, meta_compare are still supported and can be used for simple queries related to postmeta fields.

List of all metadata-related parameters:

meta_key(string)
Key (name) of custom field.
meta_value(string)
Value of custom field.
meta_value_num(int)
Numeric value of custom field.
meta_compare_key(строка) (с версии 5.1)
Operator to check value specified in meta_key parameter. Accepts: = or LIKE.
Default: '='
meta_compare(string)
Operator to check the specified value of custom field. Accepts: =, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN, EXISTS (since 3.5), NOT EXISTS (3.5), REGEXP (3.7), NOT REGEXP (3.7) и RLIKE (3.7);
Default: '='
meta_query(array)

An array of meta-data parameters defining the output (this parameter includes meta_key, meta_value and meta_compare and replaces them). Since 3.1.

You can specify the following parameters in this array:

  • relation (string)
    This parameter is specified as a string in the main meta_query array and specifies how to compare several arrays (with query parameters) specified in this main array.
    This parameter accept two values:
    OR — select meta-fields suitable for any (at least one) query parameters array.
    AND (default) — select meta-fields suitable for all query parameters arrays.

    • key (string)
      Meta key.

    • value (string/array)
      Meta value. Not necessary if compare parameter specified as: EXISTS or NOT EXISTS (since 3.9).

    • compare_key (строка) (с версии 5.1)
      Как сравнивать указанное в key значение. Accepts: = or LIKE.
      Default: '='

    • compare (string)
      How to compare the value specified in value parameter. Accepts:

      • = — equally.
      • != — not equal.
      • > — greater than.
      • >= — greater than or equal to.
      • < — less than.
      • <= — less than or equal to.
      • LIKE — the substring specified in value parameter is present in meta value as '%value%' in sql query.
      • NOT LIKE — opposite meaning of LIKE parameter.
      • IN — you specify several values in value parameter (in array) and query search for at least one of specified values.
      • NOT IN — any value except those specified in array of value parameter.
      • BETWEEN — you specify 2 values (large and small) in value parameter (in array) and query search between specified values. Example: 'value' => [5, 25] — from 5 to 25 (inclusive).
      • NOT BETWEEN — any value outside the range specified in value. Example: 'value' => [5, 25] - less than 5 and more than 25.
      • EXISTS — retrieve all posts with meta_key (specified in key parameter). 'value' parameter no need to be specified in this case. (since 3.5)
      • NOT EXISTS — retrieve all posts, except posts with meta_key (specified in key parameter). 'value' parameter no need to be specified in this case. (since 3.5).
      • REGEXP — you specifies a search regular expression in value parameter. Example: 'value'=>'^bar', (match: 'bar is'). (since 3.7)
      • NOT REGEXP — in value you specify a regular expression to search for. Gets everything that is not match to this expression. (since 3.7)
      • RLIKE — a synonym for REGEXP. (since 3.7)
    • Default: 'IN' if value is an array. '=' in other cases.*

    • type (string)
      Type of meta value (for example, if meta contains only numbers we need to specify here NUMERIC, in order to not sort numbers values as strings).

      Specified here type directly passes to mysql CAST() function.

      Can be:

      • NUMERIC — integer. You can specify the length DECIMAL(p,s).
      • DECIMAL — float number. You can specify the length DECIMAL(p,s). For example: DECIMAL(5,2) - number with maximum 5 digits (3 before separator) and 2 digits after separator.
      • SIGNED — integer (positive and negative)
      • UNSIGNED — integers (only positive)
      • CHAR — string (case-insensitive)
      • BINARY — string (case-sensitive)
      • DATETIME — date and time
      • DATE — only the date
      • TIME — only time

      If specify any other type (or don't specify type) then the type will be CHAR.

      Default: CHAR.

    The DATE type works with BETWEEN comparation only if the date is specified in the YYYY-MM-DD format and compared with the same format.

Sort by custom field

To sort by meta field, you need to specify the field data key, and then specify this key in the orderby parameter. Example:

'meta_query' => [
	'book_color' => [
		'key'     => 'color',
		'value'   => 'blue',
		'compare' => 'NOT LIKE',
	],
],
'orderby' => 'book_color',
'order'   => 'DESC',

If custom field is specified using the meta_key and meta_value parameter, specify the following values in the orderby parameter: meta_value or meta_value_num (for numbers). See description of the orderby parameter.

'meta_key=color&meta_value=blue&orderby=meta_value&order=ASC'

Basic examples (without meta_query)

#1 Posts with meta field key color, value doesn't matter:

$query = new WP_Query( 'meta_key=color' );

#2 Posts with blue value of meta field, name of the key doesn't matter:

$query = new WP_Query( 'meta_value=blue' );

#3 Posts with color meta field and blue value of this field:

$query = new WP_Query( array( 'meta_key' => 'color', 'meta_value' => 'blue' ) );

#4 Posts with color meta key and the field value is not equal to blue:

$query = new WP_Query( 
	 array( 'meta_key' => 'color', 'meta_value' => 'blue', 'meta_compare' => '!=' ) 
);

#5 Products with price meta key and meta value less than or equal to 22

If use "meta_value" parameter, the value of 99 will be greater than 100 because these values are recognized as strings (not numbers). For numbers to be interpreted correctly, use the "meta_value_num" parameter":

$query = new WP_Query( 
	 [ 'meta_key' => 'price', 'meta_value_num' => '22', 'meta_compare' => '<=', 'post_type' => 'product' ] 
);

#6 Posts with custom field value equal to 0 (no matter what the key)

$query = new WP_Query( array ( 'meta_value' => '_wp_zero_value' ) );

meta_query usage examples

#1 Posts with one custom field

Output products (product) with color meta key and not blue value:

$query = new WP_Query( [
	'post_type'  => 'product',
	'meta_query' => [
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		]
	]
] );

Note: for meta_query you must specify an array in an array, even if you specify a single query.

#2 Posts that have two custom fields at the same time

Get posts with several custom fields and specify different conditions for the values of these custom fields:

$query = new WP_Query( [
	'post_type'  => 'product',
	'meta_query' => [
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		],
		[
			'key'     => 'price',
			'value'   => [ 20, 100 ],
			'type'    => 'numeric',
			'compare' => 'BETWEEN'
		]
	]
] );

#3 Posts with at least one of the fields

Get posts with color meta key and value not (LIKE) blue or (OR) posts with price key with field values from 20 to 100:

$query = new WP_Query( [
	'post_type'    => 'product',
	'meta_query'   => [
		'relation' => 'OR',
		[
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE'
		],
		[
			'key'     => 'price',
			'value'   => [ 20, 100 ],
			'type'    => 'numeric',
			'compare' => 'BETWEEN'
		]
	]
] );

#4 Posts with two fields sorted by a third one

Suppose we have a toys post type, and in custom fields we specify the material: strong, soft toy and toy weight in grams: 100 300 500.

Get soft toys whose weight from 200 to 500 grams and sort them by price in descending order:

$query = new WP_Query( [
	'post_type'  => 'toys',
	'meta_query' => [
		[
			'key'   => 'type',
			'value' => 'soft',
		],
		[
			'key'     => 'weight',
			'value'   => [ 200, 500 ],
			'compare' => 'BETWEEN',
			'type'    => 'NUMERIC',
		],
	],
	'meta_key' => 'price',
	'orderby'  => 'meta_value_num',
	'order'    => 'DESC'
] );

#5 Get posts with specified custom field

This example shows how to get posts for which thumbnails are set (which have custom field _thumbnail_id)):

$args = array(
	'posts_per_page' => 3,
	'post_type'  => 'post',
	'meta_query' => array(
		array(
			'key'     => '_thumbnail_id',
			'compare' => 'EXISTS'
		)
	)
);
$query = new WP_Query( $args );

#6 Nested complex queries

Since 4.1, you can create subqueries. For example, let's mutch following condition: show products of orange color (color=orange) OR small (size=small) products of red color (color=red):

$args = [
	'post_type'  => 'product',
	'meta_query' => [
		'relation' => 'OR',
		[
			'key'     => 'color',
			'value'   => 'orange',
			'compare' => '=',
		],
		[
				'relation' => 'AND',
				[
						'key'     => 'color',
						'value'   => 'red',
						'compare' => '=',
				],
				[
						'key'     => 'size',
						'value'   => 'small',
						'compare' => '=',
				],
		],
	],
];
$query = new WP_Query( $args );

#7 Sort by custom field

Since 4.2, you can specify keys in meta_query and then sort by them:

$args = [
	'post_type'  => 'book',
	'meta_query' => [
		'book_color' => [
			'key'     => 'color',
			'value'   => 'blue',
			'compare' => 'NOT LIKE',
		],
	],
	'orderby' => 'book_color',
];
$query = new WP_Query( $args );
menu

Post MIME Type parameters

Selects attachments with specified MIME type. See wp_get_mime_types() for a full list of MIME types.

post_mime_type(string/array)

MIME type of the post. Used only for attachments (posts with attachment type).

This parameter will work when post_type = attachment and post_status = inherit. I.e. only attachments have MIME-type.

Get only pictures of any type

$query = new WP_Query( array(
	'post_mime_type' => 'image',
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
) );

Get only pictures of the gif type

$query = new WP_Query( array(
	'post_mime_type' => 'image/gif',
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
) );

Get all types of attachments except images

$query = new WP_Query( [
	'post_mime_type' => [ 'application', 'text', 'video', 'audio' ],
	'post_type'      => 'attachment',
	'post_status'    => 'inherit',
] );
menu

Post Status parameters

Receives posts with the specified status.

post_status(string/array)

The status of the post.

Default is publish, and if user is authorized the private status is also added. If the request is launched from the admin part protected status types are added too: future, draft and pending. All status types:

  • publish - published post.
  • pending - post on moderation.
  • draft - draft posts.
  • auto-draft - draft saved by WordPress (auto-save).
  • future - scheduled post.
  • private - personal post.
  • inherit - revision or attachment.
  • trash - removed post (in trash). Since 2.9.
  • any - all statuses, except status with parameter exclude_from_search=true. See register_post_status() and get_post_stati().

Default: 'publish'

Posts by status

Get only drafts:

$query = new WP_Query( 'post_status=draft' );

Get posts with different statuses:

$query = new WP_Query( 
	 [ 'post_status' => [ 'pending', 'draft', 'future' ] ] 
);

Get attachments with any post status:

$query = new WP_Query( 
	  [ 'post_status' => 'any', 'post_type' => 'attachment' ] 
);
menu

Date (time) parameters

Displays posts belonging to a certain period of time.

year(int)
4 year digits (e.g. 2013)
monthnum(int)
The number of the months (1 - 12)
w(int)
Week of the year (from 0 to 53)
day(int)
Day of month (1 - 31)
hour(int)
Hour (0 - 23)
minute(int)
Minute (0 - 60)
second(int)
Second (0 - 60)
m(int)
YearMonth (e.g. 201306)
date_query(array)

Date parameters to build query. Works based on WP_Date_Query class.

This parameter is specified as an array that can contain nested arrays. Parameters: column, compare, relation for the main array work as default parameters for nested arrays (if any).

For possible date formats, see PHP documentation.

  • column — database field for the query. Can be:

    post_date
    post_date_gmt
    post_modified
    post_modified_gmt
    comment_date
    comment_date_gmt
    user_registered

    Default: 'post_date'

  • compare — A comparison operator for all nested arrays by default. Can be: =, !=, >, >=, <, <=, IN, NOT IN, BETWEEN, NOT BETWEEN.
    Default: '='

  • relation — operator, if there are multiple dates arrays:
    AND (take into account simultaneously all the specified arrays).
    OR (if there is a match with at least one specified array).
    Default: OR

    The parameters below should be used in nested arrays. They define a query for a single date. All parameters below can be used in the main array as well.

    • before (string/array) — The date "to" which posts will be received. Takes a string that understand strtotime() function: all possible formats.
      Or you can pass an array with indexes: year, month, day: [ 'year'=>'2015', 'month'=>'5', 'day'=>'28' ]
      Relative to the current site time (not UTC).

    • after (string/array) — The date "after" which posts will be received. Takes a string that understand strtotime() function: all possible formats.
      Or you can pass an array with indexes: year, month, day: [ 'year' => '2015', 'month' => '5', 'day' => '28' ]
      Relative to the current site time (not UTC).

    • column (string) — see above. For specific date only. Default: the value of the upper array.

    • compare (string) — see above. For specific date only. Default: '='.

    • inclusive (true/false) — if true the before and after arguments are processed inclusive. Default: false.

    • year (int/array) — year, for example 2013.
    • dayofyear (int/array) — day of the year, 1-366.
    • month (int/array) — month, 1-12
    • week (int/array) — week, 0-53
    • day (int/array) — day, 1-31
    • dayofweek (int/array) — day of the week, 1-7, where 1 is Sunday.
    • dayofweek_iso (int/array) — day of the week, 1-7, where 1 is Monday.
    • hour (int/array) — hour, 0-23
    • minute (int/array) — minute, 0-60
    • second (int/array) — second, 0-60

    In parameters: year, month, week, dayofyear, day, dayofweek, dayofweek_iso, hour, minute, second you can specify multiple values as an array if the compare parameter allow.

#1. Get posts for today:

$today = getdate();
$query = new WP_Query( [
	'year'     => $today['year'],
	'monthnum' => $today['mon'],
	'day'      => $today['mday'],
] );

#2. Get posts for the last week:

$week = date('W');
$year = date('Y');
$query = new WP_Query( 'year=' . $year . '&w=' . $week );

#3. Get posts for 20 December:

$query = new WP_Query( 'monthnum=12&day=20' );

#4. Get posts over a period of time from 1 March to 15 March 2010:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ){
	// from March 1 to March 15, 2010
	$where .= " AND post_date >= '2010-03-01' AND post_date < '2010-03-16'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

Examples above retrieve posts for the specified period of time (in history): "Posts for x month, x day". They can't retrieve posts for any period of time in relation to the current time. Therefore, requests such as "Posts for the last 30 days" or "Posts for the last year" are not possible in the basic version. For such requests, you need to use posts_where filter. The examples below show how to do this.

#5. Get posts for the last 30 days:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ) {
	// for the last 30 days
	$where .= " AND post_date > '" . date('Y-m-d', strtotime('-30 days')) . "'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

#6. Get posts for the interval from 30 to 60 days, up to the present:

// Create a new function that adds a where clause to the query
function where_clause__filter( $where = '' ) {
	// 30 to 60 days
	$where .= " AND post_date >= '" . date('Y-m-d', strtotime('-60 days')) . "'" . " AND post_date <= '" . date('Y-m-d', strtotime('-30 days')) . "'";
	return $where;
}

add_filter( 'posts_where', 'where_clause__filter' );
$query = new WP_Query( $query_string );
remove_filter( 'posts_where', 'where_clause__filter' );

m parameter can be set only for posts list in the admin panel. It is useful when you select from the select drop-down list where the date is set as YYYYmm number.

Examples with parameter date_query

#1. Get posts between 9 and 17 hours:

$args = [
	'date_query' => [
		[
			'hour'      => 9,
			'compare'   => '>=',
		],
		[
			'hour'      => 17,
			'compare'   => '<=',
		],
		[
			'dayofweek' => [ 2, 6 ],
			'compare'   => 'BETWEEN',
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#2. Get posts for the time period: 1 January to 28 February:

Dates can be specified as numbers and strings, because the arguments before and after are handled by the PHP function strtotime(), it understands date in a string format.

$args = [
	'date_query' => [
		[
			'after'     => 'January 1st, 2013',
			'before'    => [
				'year'  => 2013,
				'month' => 2,
				'day'   => 28,
			],
			'inclusive' => true,
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#3. Get posts published a year ago, but changed in the last month:

$args = [
	'date_query' => [
		[
			'column' => 'post_date_gmt',
			'before' => '1 year ago',
		],
		[
			'column' => 'post_modified_gmt',
			'after'  => '1 month ago',
		],
	],
	'posts_per_page' => -1,
];
$query = new WP_Query( $args );

#4. Get posts for the last 2 weeks using the string '2 weeks ago':

$query = new WP_Query( [
	'date_query' => [
		'after' => '2 weeks ago',
	],
] );

#5. Get posts for the last 2 months, published on weekdays:

$query = new WP_Query( [
	'date_query' => [
		'after' => '2 months ago',
		[
			'dayofweek' => [ 1, 5 ],
			'compare' => 'BETWEEN',
		],
	],
] );

The date_query argument works with the WP_Comment_Query class as well, so it can be used in get_comments() function.

menu

Offset parameters

You can indent from first post in the query results. For example, a standard query returns 6 posts, and if you add the parameter offset=1 to the same query, 5 posts will be returned (the first one will be skipped).

offset(int)
How many posts from the beginning of the query result to skip.

Usage example

Skip the first post (offset=1) and return the next 5:

$query = new WP_Query( 'posts_per_page=5&offset=1' );

Sort/Order parameters

Sorts and sets the sort direction.

Sorting parameters not work if you specify fields = ids, because in this case, the query will not have fields by which it can sort the result.

order(string)

The sort direction for the orderby parameter. Can be:

  • ASC - in order, from less to more (1, 2, 3 or a, b, c).
  • DESC - in reverse order, from more to less (3, 2, 1 or c, b, a).
orderby(string/array)

Fields on which you can sort posts. Can be:

  • none — do not sort, output as is in the database. Equivalent to sorting by ID. Since 2.8.
  • ID — sort by ID.
  • author — sort by author ID.
  • title — sort by title.
  • name — by post name (post slug).
  • date — sort by publication date.
  • modified — sort by modification date.
  • type — by post type (post_type). Since 4.0.
  • parent — sort by the value of the post_parent field.
  • rand — random order.
  • RAND(x) — in a random order for numeric values. Here "x" is an integer.
  • comment_count — sort by the number of comments. Since 2.9.
  • relevance — by search terms (parameter s) in the following order: 1) whether the entire sentence is matched. 2) all search terms are in the post title. 3) any of the search terms appear in the post title. 4) the full sentence is found in the post content.
  • menu_order — used for pages and attachments. The sequence number is specified on the edit-post admin page.
  • meta_value — by the value of a custom field.

    Important: the meta_key parameter must also be defined. Note: the sorting will be alphabetical and it's not logical if the values of custom field is a numbers (for example 1, 3, 34, 4, 56, 6 etc., not 1, 3, 4, 6, 34, 56).

  • meta_value_num — sort by custom field whose values are numbers. Since 2.8.
  • array key from meta_query — in this case, the sorting will be based on the value of custom field specified in the meta_query array.
  • post__in — takes into account the order of ID specified in post__in parameter. order parameter is ignored
  • post_name__in — takes into account the order of names specified in the post_name__in parameter. order parameter is ignored
  • post_parent__in — takes into account the order of ID specified in post_parent__in parameter. order parameter is ignored.
orderby = array()

Since WordPress 4.0 you can specify the combination of orderby and order parameters as an array in orderby parameter. It is for sorting by several columns simultaneously. Syntax is:

'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' )

#1 Sort by title

$query = new WP_Query( [ 'orderby' => 'title', 'order' => 'DESC' ] );

Sort by menu order and then by title

$query = new WP_Query( [ 'orderby' => 'menu_order title', 'order' => 'DESC' ] );

#2 Output one random post:

$query = new WP_Query( [ 'orderby' => 'rand', 'posts_per_page' => '1' ] );

#3 Sort posts by number of comments:

$query = new WP_Query( [ 'orderby' => 'comment_count' ] );

#4 Sort products by price (price custom field):

$query = new WP_Query( 
	 array ( 'post_type' => 'product', 'orderby' => 'meta_value', 'meta_key' => 'price' ) 
);

#5 Multiple sorting

Output posts sorted by two fields: 'title' and 'menu_order' (title is paramount):

$query = new WP_Query( 
	array( 'post_type' => 'page', 'orderby' => 'title menu_order', 'order' => 'ASC' ) 
);

#6 Multiple sorting using an array

Get pages sorted by title (post_title) and menu number (menu_order) in different order (ASC/DESC):

$query = new WP_Query( [ 'orderby' => array( 'title' => 'DESC', 'menu_order' => 'ASC' ) ] );

#7 Sorting by 'meta_value' for custom post type

Output posts of 'my_custom_post_type' type sorted by 'age' custom field and filtered to show posts with custom field value 3 and 4:

$args = [
   'post_type'  => 'my_custom_post_type',
   'meta_key'   => 'age',
   'orderby'    => 'meta_value_num',
   'order'      => 'ASC',
   'meta_query' => [
	   [
		   'key'     => 'age',
		   'value'   => [ 3, 4 ],
		   'compare' => 'IN',
	   ]
   ]
];
$query = new WP_Query( $args );

#8 Sorting by multiple meta_keys

To order by two different custom fields (for example, city first and state second), you need to set array keys for meta_query sub-arrays and then use this keys in orderby array:

$query = new WP_Query( [
	'meta_query' => [
		'relation'     => 'AND',
		'state_clause' => [
			'key'   => 'state',
			'value' => 'Wisconsin',
		],
		'city_clause' => [
			'key'     => 'city',
			'compare' => 'EXISTS',
		], 
	],
	'orderby' => [
		'city_clause'  => 'ASC',
		'state_clause' => 'DESC',
	],
] );
menu

Pagination parameters

nopaging(true/false)
Turn off pagination, displays all posts on one page.
posts_per_page(int)

Number of posts per page. If set "-1", all posts will be displayed (without pagination).

Since 2.1 replaces the showposts parameter. Set the paged parameter if pagination does not work after using this parameter.

Note: if the query made in the feed part, WP overwrites this parameter with the posts_per_rss option. To affect the output of the feed, use post_limits or pre_option_posts_per_rss filters.

posts_per_archive_page(int)
Number of posts for archive pages: for pages that meet the conditions is_archive() or is_search(). This option overwrites parameters posts_per_page and showposts.
offset(int)

How many posts to skip from the top of the result (top indentation).

Important: Setting this parameter may overwrites/ignores the paged parameter and breaks the pagination.

paged(int)
Pagination page number. Shows posts that normally have to be shown on X pagination page. Overwrites posts_per_page parameter.
page(int)
Pagination page number for the static home page. Shows posts that normally have to be shown on X pagination page of the main static page (front page).
ignore_sticky_posts(true/false)

Ignore sticky posts or not (true/false).

Sticky posts will not appear at the top of the list, but they are not excluded and will be shown in usual order.

#1 Posts per page

Get 3 posts:

$query = new WP_Query( 'posts_per_page=3' );

Get all posts:

$query = new WP_Query( 'posts_per_page=-1' );

Get all posts and disable pagination:

$query = new WP_Query( 'nopaging=true' );

#2 The indentation from the top

Get posts starting with the fourth (skip the first 3):

$query = new WP_Query( 'offset=3' ) );

Get 5 posts that follow after first 3 posts:

$query = new WP_Query( [ 'posts_per_page' => 5, 'offset' => 3 ] );

#3 Posts from the pagination page 6

$query = new WP_Query( 'paged=6' );

#4 Posts from current pagination page

Useful for building custom pagination:

$query = new WP_Query( [ 'paged' => get_query_var( 'paged' ) ] );

Get posts from the current pagination page and set the paged parameter to 1 when the variable is not defined on the first page of pagination:

$paged = get_query_var('paged') ?: 1;
$query = new WP_Query( [ 'paged' => $paged ] );

Use get_query_var('page'); if you want to get the pagination page number for a static home page (is_front_page()). The "page" query variable contains pagination page number, when the <!--nextpage--> tag is used in post content to split post content.

Get posts of pagination page for static home page:

$paged = get_query_var('page') ?: 1;
$query = new WP_Query( [ 'paged' => $paged ] );

#5 Sticky posts

# All sticky posts
$sticky = get_option( 'sticky_posts' );
$query = new WP_Query( [ 'post__in' => $sticky ] );
# Only first sticky post
$sticky = get_option( 'sticky_posts' );
$query = new WP_Query( 'p=' . $sticky[0] );
# First sticky post. If there is no sticky post, then the last published
$query = new WP_Query( array(
	'posts_per_page'      => 1,
	'post__in'            => get_option( 'sticky_posts' ),
	'ignore_sticky_posts' => 1
) );
# First sticky post. If there is no sticky post, then nothing is output
$sticky = get_option( 'sticky_posts' );

if ( ! empty($sticky[0]) ) {

	$query = new WP_Query( array(
		'posts_per_page'      => 1,
		'post__in'            => $sticky,
		'ignore_sticky_posts' => 1
	) );

	// output...
}
# 5 last sticky posts
$sticky = get_option( 'sticky_posts' ); // all Sticky posts

rsort( $sticky ); // sort - new at the top

$sticky = array_slice( $sticky, 0, 5 ); // get the first 5

$query = new WP_Query( [ 'post__in'=>$sticky, 'ignore_sticky_posts'=>1 ] );

#6 Hide sticky posts

Exclude all sticky posts from the query:

$query = new WP_Query( [ 'post__not_in' => get_option( 'sticky_posts' ) ] );

Exclude sticky posts from the category. The following example returns all posts from the category, but sticky posts will be shown as regular posts (by date):

$query = new WP_Query( [ 'ignore_sticky_posts' => 1, 'posts_per_page' => 3, 'cat' => 6 ] );

Exclude sticky posts from the category. Next example return all posts from the category, and sticky posts will be excluded. Also, add a rule for proper pagination:

$paged  = get_query_var( 'paged' ) ?: 1;
$sticky = get_option( 'sticky_posts' );
$query  = new WP_Query( array(
	'cat'                 => 3,
	'ignore_sticky_posts' => 1,
	'post__not_in'        => $sticky,
	'paged'               => $paged
) );
menu

Comment parameters

The parameters below allow to get posts by some comments data. Also, comments data are added into the WP_Query object if the query made for a single post; and some parameters below control such comments.

comment_status(string)
Status of the comment.
ping_status(int)
Status of the ping.
comments_per_page(int)
Number of comments to retrieve for a separate comment page. By default value of WP option comments_per_page.
comment_count(int/array)

Number of comments retrieved posts should have. Since 4.9.

  • Number — if specify a number, query retrieve posts with the specified number of comments (search operator =).
  • Array — you can specify an array, it allows you to set option how to compare specified number. Allowed array keys:
    • value (int) — number of comments posts should have.
    • compare (string) — how to compare specified number. Can be: = (default), !=, >, >=, <, <=.
menu

Search parameters

s(string)
Search phrase.
exact(true/false)
true — search by exact phrase specified in s parameter. Without adding % at the ends of the search phrase in SQL query.
Default: false
sentence(true/false)
true — search for the full search phrase, just as it is.
false — the search phrase is divided into words and the search is based on the words from the search phrase.
Default: false

Get posts by search phrase:

$query = new WP_Query( 's=search phrase' );

Returned fields parameters

Sets which fields the query should return.

fields(string/array)

What fields to return. By default, all fields are returned.

  • ids - returns an array with post ID.
  • id=>parent - returns an associative array [ parent => ID, … ].
  • Specifying any other value will return all fields (default) - array of post objects.
no_found_rows(true/false)
true — don't count the number of found rows. In some cases, it may speed up the request.
Default: false

Cache parameters

The parameters below control the data addition to the object cache when executing queries.

cache_results(true/false)
Whether to cache information about the post.
Default: true
update_post_meta_cache(true/false)
Whether to cache information about postmeta data.
Default: true
update_post_term_cache(true/false)
Whether to cache information aboute post terms and taxonomies.
Default: true
lazy_load_term_meta(true/false)
Whether to lazy-load term meta. Setting to false will disable cache priming for term meta, so that each get_term_meta() call will hit the database.
Default: value of $update_post_term_cache

#1 Get 50 posts, but don't add information about posts to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'cache_results' => false ] );

#2 Get 50 posts, but don't add meta data of the post to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'update_post_meta_cache' => false ] );

#3 Get 50 posts, but don't add information about the terms of posts to the cache:

$query = new WP_Query( [ 'posts_per_page' => 50, 'update_post_term_cache' => false ] );

Usually, this functionality do not need to be used — the cache is necessary! However, this approach can be useful in some cases. For example, if you need to get a list of post titles and don't need any other information about posts: neither about taxonomies nor about meta data. You can avoid unnecessary SQL queries if do not load this unnecessary information.

Note: if a persistent cache plugin is used, all these parameters are set to false by default because there is no need to update the cache every time the page loads.

menu

Filters parameters

suppress_filters(true/false)

Whether to suppress filters — disable work of some hooks of WP_Query class. Enabling this parameter cancels all SQL query modification filters.

true — turn off handling of following filters:

posts_search
posts_search_orderby

posts_where
posts_join

posts_where_paged
posts_groupby
posts_join_paged
posts_orderby
posts_distinct
post_limits
posts_fields
posts_clauses

posts_where_request
posts_groupby_request
posts_join_request
posts_orderby_request
posts_distinct_request
posts_fields_request
post_limits_request
posts_clauses_request

posts_request
posts_results
the_posts

comment_feed_join
comment_feed_where
comment_feed_groupby
comment_feed_orderby
comment_feed_limits

Default: false

Access parameters

Get the posts if the current user has sufficient permissions.

perm(string)
User capability.

Output published private posts, if the user has sufficient rights to view:

$query = new WP_Query( 
	  [ 'post_status' => [ 'publish', 'private' ], 'perm' => 'readable' ] 
);

Parameters Combination

#1. Combining in a string

You may have noticed in some examples above that parameters are joined by the ampersand symbol - &. By this symbol, parameters can be joined (combined).

Posts from category 3 for 2004 year:

$query = new WP_Query( 'cat=3&year=2004' );

Get all posts from category 1 with the apples tag:

$query = new WP_Query( 'cat=1&tag=apples' );

#2. Adding parameters to the query

Get posts from category 13 for the current month on the main site page:

if ( is_home() ) {
	$query = new WP_Query( $query_string . '&cat=13&monthnum=' . date( 'n', current_time('timestamp') ) );
}

#3. Parameters in an array

Get 2 posts, from category 1 and 3, sorted in reverse order by title:

$query = new WP_Query( [
   'category__and'  => [ 1,3 ],
   'posts_per_page' => 2,
   'orderby'        => 'title',
   'order'          => 'DESC',
] );

Examples

#1. Exclude posts from the category 3 on the main page of the site

To exclude posts that are in category 3 on the main page of the blog, you need to insert the following code into the index.php file before the WordPress Loop:

<?php
if ( is_home() ) {
	query_posts( $query_string .'&cat=-3' );
}
?>

#1.2. You can add more categories:

<?php
if ( is_home() ) {
	query_posts( $query_string .'cat=-1,-2,-3' );
}
?>

#2. Get a certain post (post with ID = 5):

<?php
// retrieve one post with an ID of 5
$query = new WP_Query( 'p=5' );
?>

#3. If you want to use the read more function with a new query, you need switch the global variable $more to 0:

<?php
// retrieve one post with an ID of 5
$query = new WP_Query( 'p=5' );

global $more;
// set $more to 0 in order to only get the first part of the post
$more = 0; 

// the Loop
while ( have_posts() ) : the_post();
  // the content of the post
  the_content( 'Read the full post »' );
endwhile;
?>

#4. Get a specific page (page 7):

<?php
$query = new WP_Query('page_id=7');      // get only page with ID 7
?>

or

<?php
$query = new WP_Query('pagename=aboute-site'); // get only the "aboute-site" page
?>

#4.1. For child pages, you need specify the name of the parent page and the child page itself.

Names are separated by a slash (/). Example:

<?php
$query = new WP_Query( 'pagename=aboute-site/authors'); // get page "authors", which is child page of the "aboute-site"
?>

Inserting variables into query parameters

You can create dynamic query parameters, if you need change query depending on any circumstances. To do this save parameter value to a variable, and then pass this variable to the query parameters. You can do it in several ways:

#1. Building a query using single quotes ' ':
<?php
$category_var = $cat;
$query = 'cat=' . $category_var . '&orderby=date&order=ASC'; // collect query
$query = new WP_Query( $query ); // run a query
?>
#2. Building a query using double quotes " "

Variables inside double quotes are interpreted by PHP as variables, not as plain text:

<?php
$current_month = date('m');
$current_year  = date('Y');

$query = new WP_Query( "cat=22&year=$current_year&monthnum=$current_month&order=ASC" );
?>
<!-- WordPress Loop here -->
#3. Using global variable $query_string

That contains a basic query for the query_posts() function. If you don't need to break the standard WordPress posts output (for example, on the category page), but need to remove the pagination (display all posts on one page), we can extend the basic query_posts() parameters with posts_per_page=-1:

<?php
global $wp_query;
$wp_query = new WP_Query( $query_string . '&posts_per_page=-1' );
while( have_posts() ){
	the_post();
	// here is the WordPress Loop
}

wp_reset_query(); // reset global $wp_query
?>

You can change the value of posts_per_page to a specific number of posts you need on a single page. For example, posts_per_page=10 displays only 10 posts, and if you put the template tag posts_nav_link() at the end of the loop, a link will appear below the loop to go to the next 10 posts (pagination link).

#4. Parameters can also be passed as an array

Specifying parameters as an array is more readable. Example 2 can be written as:

$query = new WP_Query( array(
	'cat'      => 22,
	'year'     => $current_year,
	'monthnum' => $current_month,
	'order'    => 'ASC',
) );

As you can see you can put each variable on a separate line, and it is more readable.

Adding parameters to a query

Save the base query of the current page and add your own parameters to it.

The query_posts() function completely overwrites the main WP query, if, for example, we write query_posts('cat=1'), then other query parameters that are used for the current page (for example, sorting, pagination, etc.) will be lost and posts of category 1 will be displayed only. To save the basic parameters of the query and add/replace them with your own, you need to use array_merge() PHP function (combines 2 arrays into one):

global $wp_query;
query_posts(
	array_merge(
		array( 'cat' => 1 ), // this is the parameter we add
		$wp_query->query     // this is the base request array of the current page
		это массив базового запроса текущей страницы
	)
);

This example is essentially the same as example 3 (using the global variable $query_string), but uses array to set query parameters.

menu

Class methods and properties

WP_Query properties

$query
Stores the query string.
$query_vars
Associative array with query data.
$tax_query
A query for a taxonomy. Object that is passed to get_tax_sql().
$meta_query
Request parameters for custom fields.
$date_query
Request parameters for dates.
$queried_object
Used in queries such as category, author, post or page. Contains information about category, author, post, or page.
$queried_object_id
If the request relates to a category, author, post or page, it contains the corresponding ID.
$request
Full SQL query that was built on the basis of the passed query parameters.
$posts
Filled with posts data obtained from Database.
$post_count
The number of posts displayed on a page.
$current_post
Available during the loop. The index of the current post that is displayed.
$in_the_loop
Logical variable. Determines whether the loop is started and the called object is in the loop.
$post
Available during the loop. Post which is displayed at the moment.
$comments
List of comments for the current post.
$comment_count
Number of comments for the current post.
$current_comment
The current comment in the comment loop.
$comment
ID of the current comment.
$found_posts
Number of all posts in the query.
$max_num_pages
Count of pagination pages: $found_posts / $posts_per_page
$is_single, $is_page, $is_archive, $is_preview, $is_date, $is_year, $is_month, $is_time, $is_author, $is_category, $is_tag, $is_tax, $is_search, $is_feed, $is_comment_feed, $is_trackback, $is_home, $is_404, $is_comments_popup, $is_admin, $is_attachment, $is_singular, $is_robots, $is_posts_page, $is_paged
Boolean value. Determine which page type the current request belongs to.
$stopwords
Cached list of stopwords for search query.

WP_Query Class methods

Ampersand & before the method, calls it as link.

  1. init_query_flags()
  2. init()
  3. parse_query_vars()
  4. fill_query_vars( $array )
  5. parse_query( $query = '' )
  6. parse_tax_query( &$q )
  7. parse_search( &$q )
  8. parse_search_terms( $terms )
  9. get_search_stopwords()
  10. parse_search_order( &$q )
  11. parse_orderby( $orderby )
  12. parse_order( $order )
  13. set_404()
  14. get( $query_var, $default = '' )
  15. set( $query_var, $value )
  16. get_posts()
  17. set_found_posts( $q, $limits )
  18. next_post()
  19. the_post()
  20. have_posts()
  21. rewind_posts()
  22. next_comment()
  23. the_comment()
  24. have_comments()
  25. rewind_comments()
  26. query( $query )
  27. get_queried_object()
  28. get_queried_object_id()
  29. __construct( $query = '' )
  30. __get( $name )
  31. __isset( $name )
  32. __call( $name, $arguments )
  33. is_archive()
  34. is_post_type_archive( $post_types = '' )
  35. is_attachment( $attachment = '' )
  36. is_author( $author = '' )
  37. is_category( $category = '' )
  38. is_tag( $tag = '' )
  39. is_tax( $taxonomy = '', $term = '' )
  40. is_comments_popup()
  41. is_date()
  42. is_day()
  43. is_feed( $feeds = '' )
  44. is_comment_feed()
  45. is_front_page()
  46. is_home()
  47. is_privacy_policy()
  48. is_month()
  49. is_page( $page = '' )
  50. is_paged()
  51. is_preview()
  52. is_robots()
  53. is_search()
  54. is_single( $post = '' )
  55. is_singular( $post_types = '' )
  56. is_time()
  57. is_trackback()
  58. is_year()
  59. is_404()
  60. is_embed()
  61. is_main_query()
  62. setup_postdata( $post )
  63. generate_postdata( $post )
  64. reset_postdata()
  65. lazyload_term_meta( $check, $term_id )
  66. lazyload_comment_meta( $check, $comment_id )

Changelog

Since 1.5.0 Introduced.
Since 4.5.0 Removed the $comments_popup property.

Code of WP Query: wp-includes/class-wp-query.php WP 5.2.4

The code is too large, see it on single page...

Related Functions

From tag: query

More from category: Classes

1 comment
  • Mohsin www.snippet99.com/2019/09/10/wp-loop-a-b...

    Very informative and detailed article, thumbs up.

Hello, !     Log In . Register