Query Arguments

The argument array that turns WP_Query into the query you want: type, count, order, offset, dates and fields.

Syntaxnew WP_Query( array( 'post_type' => 'post', 'posts_per_page' => 10, 'orderby' => 'date', 'order' => 'DESC' ) )

Everything WP_Query does is driven by its argument array. The arguments are documented on WP_Query::parse_query(), and the same names work in get_posts(), pre_get_posts and the REST controllers, so learning them once pays off everywhere.

The arguments you use daily

post_type takes a slug or an array of slugs. posts_per_page is the page size; -1 requests all posts. order accepts 'ASC' or 'DESC' and defaults to 'DESC'. orderby accepts 'date' (the default), 'title', 'ID', 'name', 'menu_order', 'rand', 'comment_count', 'meta_value', 'meta_value_num', 'post__in' and more. post__in takes an array of IDs and includes sticky posts; post__not_in excludes them, and both reject comma-separated strings. offset skips rows before retrieval. date_query is an array of WP_Date_Query clauses, whose column defaults to 'post_date' and whose inclusive defaults to false. fields accepts '' for WP_Post objects, 'ids' for post IDs, or 'id=>parent'.

Shorthand and longhand

WP_Query also accepts a URL-style query string. new WP_Query( 'post_type=book&posts_per_page=5' ) is parsed with wp_parse_args() into exactly the array form. The string is shorter but cannot express arrays, so use it only for trivial cases.

What trips people up

offset and paged do not combine: an explicit offset overrides the LIMIT that paged would have produced, so page two silently shows page one.

Example

Example · php
<?php
// Upcoming events: custom post type, ordered by a date stored in post meta.
$events = new WP_Query(
	array(
		'post_type'      => array( 'event', 'workshop' ), // slug or array of slugs
		'post_status'    => 'publish',
		'posts_per_page' => 12,

		// Ordering by a meta value requires meta_key to name the field.
		// 'meta_value_num' casts to a number; 'meta_value' compares as a string.
		'meta_key'       => 'event_start',
		'orderby'        => 'meta_value_num',
		'order'          => 'ASC',

		// Published in the last year only. 'column' defaults to 'post_date'.
		'date_query'     => array(
			array(
				'after'     => '1 year ago', // any strtotime()-compatible string
				'inclusive' => true,         // default is false
			),
		),

		'post__not_in'   => array( 42 ), // array of IDs; a CSV string will not work
	)
);

When to use it

  • List a custom post type ordered by a meta field such as an event date
  • Render a curated carousel in a hand-picked ID order
  • Restrict an archive to posts published within a date range
  • Fetch a flat array of post IDs for an export or cache-warming routine

More examples

Query-string shorthand and its array longhand

You will meet the string form in older themes and in shortcode handlers, so recognise it as the same thing as the array. Write the array form yourself: it is diff-friendly, and every non-trivial argument (post__in, meta_query, tax_query, date_query) needs an array anyway.

Example · php
<?php
// Shorthand: a URL-style string, run through wp_parse_args() internally.
$q = new WP_Query( 'post_type=book&posts_per_page=5&orderby=title&order=ASC' );

// Longhand: the identical query. This is what the string parses into.
$q = new WP_Query(
	array(
		'post_type'      => 'book',
		'posts_per_page' => 5,
		'orderby'        => 'title',
		'order'          => 'ASC',
	)
);

// The string form cannot express an array, so this is impossible as a string
// and must be written as an array:
$q = new WP_Query(
	array(
		'post_type' => array( 'book', 'review' ),
		'post__in'  => array( 12, 34, 56 ),
	)
);

Preserving an explicit ID order

Without 'orderby' => 'post__in' the results come back in date order and your curated sequence is lost. This is the standard shape for editor-chosen carousels and 'featured' blocks driven by an options page.

Example · php
<?php
$featured_ids = array( 88, 12, 45 ); // hand-picked order from an option

$featured = new WP_Query(
	array(
		'post_type'              => 'any',
		'post__in'               => $featured_ids,
		'orderby'                => 'post__in', // keep the array's order
		'posts_per_page'         => count( $featured_ids ),
		'ignore_sticky_posts'    => true,  // otherwise stickies jump to the front
		'update_post_term_cache' => false, // no taxonomy output in this block
	)
);

Fetching only what you need with 'fields'

When a task only needs identifiers — a cache key, an export list, a membership check — hydrating full WP_Post objects wastes memory and queries. Be careful with 'posts_per_page' => -1 on tables that can grow without bound.

Example · php
<?php
// 'ids' returns int[]; '' (the default) returns WP_Post[];
// 'id=>parent' returns parent IDs keyed by post ID.
$ids = get_posts(
	array(
		'post_type'      => 'product',
		'posts_per_page' => -1, // -1 means every match
		'fields'         => 'ids',
		'post_status'    => 'publish',
	)
);

// Now prime the cache once instead of hitting the DB per post.
_prime_post_caches( $ids, false, true );

foreach ( $ids as $id ) {
	echo esc_html( get_the_title( $id ) );
}

Discussion

  • Be the first to comment on this lesson.