WP_REST_Posts_Controller::get_items( WP_REST_Request $request ): WP_REST_Response|WP_Error

In this article

Retrieves a collection of posts.

Parameters

$requestWP_REST_Requestrequired
Full details about the request.

Return

WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.

Source

public function get_items( $request ) {	// Ensure a search string is set in case the orderby is set to 'relevance'.	if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) {	return new WP_Error(	'rest_no_search_term_defined',	__( 'You need to define a search term to order by relevance.' ),	array( 'status' => 400 )	);	}	// Ensure an include parameter is set in case the orderby is set to 'include'.	if ( ! empty( $request['orderby'] ) && 'include' === $request['orderby'] && empty( $request['include'] ) ) {	return new WP_Error(	'rest_orderby_include_missing_include',	__( 'You need to define an include parameter to order by include.' ),	array( 'status' => 400 )	);	}	// Retrieve the list of registered collection query parameters.	$registered = $this->get_collection_params();	$args = array();	/* * This array defines mappings between public API query parameters whose * values are accepted as-passed, and their internal WP_Query parameter * name equivalents (some are the same). Only values which are also * present in $registered will be set. */	$parameter_mappings = array(	'author' => 'author__in',	'author_exclude' => 'author__not_in',	'exclude' => 'post__not_in',	'include' => 'post__in',	'ignore_sticky' => 'ignore_sticky_posts',	'menu_order' => 'menu_order',	'offset' => 'offset',	'order' => 'order',	'orderby' => 'orderby',	'page' => 'paged',	'parent' => 'post_parent__in',	'parent_exclude' => 'post_parent__not_in',	'search' => 's',	'search_columns' => 'search_columns',	'slug' => 'post_name__in',	'status' => 'post_status',	);	/* * For each known parameter which is both registered and present in the request, * set the parameter's value on the query $args. */	foreach ( $parameter_mappings as $api_param => $wp_param ) {	if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {	$args[ $wp_param ] = $request[ $api_param ];	}	}	// Check for & assign any parameters which require special handling or setting.	$args['date_query'] = array();	if ( isset( $registered['before'], $request['before'] ) ) {	$args['date_query'][] = array(	'before' => $request['before'],	'column' => 'post_date',	);	}	if ( isset( $registered['modified_before'], $request['modified_before'] ) ) {	$args['date_query'][] = array(	'before' => $request['modified_before'],	'column' => 'post_modified',	);	}	if ( isset( $registered['after'], $request['after'] ) ) {	$args['date_query'][] = array(	'after' => $request['after'],	'column' => 'post_date',	);	}	if ( isset( $registered['modified_after'], $request['modified_after'] ) ) {	$args['date_query'][] = array(	'after' => $request['modified_after'],	'column' => 'post_modified',	);	}	// Ensure our per_page parameter overrides any provided posts_per_page filter.	if ( isset( $registered['per_page'] ) ) {	$args['posts_per_page'] = $request['per_page'];	}	if ( isset( $registered['sticky'], $request['sticky'] ) ) {	$sticky_posts = get_option( 'sticky_posts', array() );	if ( ! is_array( $sticky_posts ) ) {	$sticky_posts = array();	}	if ( $request['sticky'] ) {	/* * As post__in will be used to only get sticky posts, * we have to support the case where post__in was already * specified. */	$args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts;	/* * If we intersected, but there are no post IDs in common, * WP_Query won't return "no posts" for post__in = array() * so we have to fake it a bit. */	if ( ! $args['post__in'] ) {	$args['post__in'] = array( 0 );	}	} elseif ( $sticky_posts ) {	/* * As post___not_in will be used to only get posts that * are not sticky, we have to support the case where post__not_in * was already specified. */	$args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts );	}	}	/* * Honor the original REST API `post__in` behavior. Don't prepend sticky posts * when `post__in` has been specified. */	if ( ! empty( $args['post__in'] ) ) {	unset( $args['ignore_sticky_posts'] );	}	if (	isset( $registered['search_semantics'], $request['search_semantics'] )	&& 'exact' === $request['search_semantics']	) {	$args['exact'] = true;	}	$args = $this->prepare_tax_query( $args, $request );	if ( isset( $registered['format'], $request['format'] ) ) {	$formats = $request['format'];	/* * The relation needs to be set to `OR` since the request can contain * two separate conditions. The user may be querying for items that have * either the `standard` format or a specific format. */	$formats_query = array( 'relation' => 'OR' );	/* * The default post format, `standard`, is not stored in the database. * If `standard` is part of the request, the query needs to exclude all post items that * have a format assigned. */	if ( in_array( 'standard', $formats, true ) ) {	$formats_query[] = array(	'taxonomy' => 'post_format',	'field' => 'slug',	'operator' => 'NOT EXISTS',	);	// Remove the `standard` format, since it cannot be queried.	unset( $formats[ array_search( 'standard', $formats, true ) ] );	}	// Add any remaining formats to the formats query.	if ( ! empty( $formats ) ) {	// Add the `post-format-` prefix.	$terms = array_map(	static function ( $format ) {	return "post-format-$format";	},	$formats	);	$formats_query[] = array(	'taxonomy' => 'post_format',	'field' => 'slug',	'terms' => $terms,	'operator' => 'IN',	);	}	// Enable filtering by both post formats and other taxonomies by combining them with `AND`.	if ( isset( $args['tax_query'] ) ) {	$args['tax_query'][] = array(	'relation' => 'AND',	$formats_query,	);	} else {	$args['tax_query'] = $formats_query;	}	}	// Force the post_type argument, since it's not a user input variable.	$args['post_type'] = $this->post_type;	$is_head_request = $request->is_method( 'HEAD' );	if ( $is_head_request ) {	// Force the 'fields' argument. For HEAD requests, only post IDs are required to calculate pagination.	$args['fields'] = 'ids';	// Disable priming post meta for HEAD requests to improve performance.	$args['update_post_term_cache'] = false;	$args['update_post_meta_cache'] = false;	}	/** * Filters WP_Query arguments when querying posts via the REST API. * * The dynamic portion of the hook name, `$this->post_type`, refers to the post type slug. * * Possible hook names include: * * - `rest_post_query` * - `rest_page_query` * - `rest_attachment_query` * * Enables adding extra arguments or setting defaults for a post collection request. * * @since 4.7.0 * @since 5.7.0 Moved after the `tax_query` query arg is generated. * * @link https://developer.wordpress.org/reference/classes/wp_query/ * * @param array $args Array of arguments for WP_Query. * @param WP_REST_Request $request The REST API request. */	$args = apply_filters( "rest_{$this->post_type}_query", $args, $request );	$query_args = $this->prepare_items_query( $args, $request );	$posts_query = new WP_Query();	$query_result = $posts_query->query( $query_args );	// Allow access to all password protected posts if the context is edit.	if ( 'edit' === $request['context'] ) {	add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );	}	if ( ! $is_head_request ) {	$posts = array();	update_post_author_caches( $query_result );	update_post_parent_caches( $query_result );	if ( post_type_supports( $this->post_type, 'thumbnail' ) ) {	update_post_thumbnail_cache( $posts_query );	}	foreach ( $query_result as $post ) {	if ( ! $this->check_read_permission( $post ) ) {	continue;	}	$data = $this->prepare_item_for_response( $post, $request );	$posts[] = $this->prepare_response_for_collection( $data );	}	}	// Reset filter.	if ( 'edit' === $request['context'] ) {	remove_filter( 'post_password_required', array( $this, 'check_password_required' ) );	}	$page = isset( $query_args['paged'] ) ? (int) $query_args['paged'] : 0;	$total_posts = $posts_query->found_posts;	if ( $total_posts < 1 && $page > 1 ) {	// Out-of-bounds, run the query again without LIMIT for total count.	unset( $query_args['paged'] );	$count_query = new WP_Query();	$count_query->query( $query_args );	$total_posts = $count_query->found_posts;	}	$max_pages = (int) ceil( $total_posts / (int) $posts_query->query_vars['posts_per_page'] );	if ( $page > $max_pages && $total_posts > 0 ) {	return new WP_Error(	'rest_post_invalid_page_number',	__( 'The page number requested is larger than the number of pages available.' ),	array( 'status' => 400 )	);	}	$response = $is_head_request ? new WP_REST_Response( array() ) : rest_ensure_response( $posts );	$response->header( 'X-WP-Total', (int) $total_posts );	$response->header( 'X-WP-TotalPages', (int) $max_pages );	$request_params = $request->get_query_params();	$collection_url = rest_url( rest_get_route_for_post_type_items( $this->post_type ) );	$base = add_query_arg( urlencode_deep( $request_params ), $collection_url );	if ( $page > 1 ) {	$prev_page = $page - 1;	if ( $prev_page > $max_pages ) {	$prev_page = $max_pages;	}	$prev_link = add_query_arg( 'page', $prev_page, $base );	$response->link_header( 'prev', $prev_link );	}	if ( $max_pages > $page ) {	$next_page = $page + 1;	$next_link = add_query_arg( 'page', $next_page, $base );	$response->link_header( 'next', $next_link );	}	return $response; } 

Hooks

apply_filters( “rest_{$this->post_type}_query”, array $args, WP_REST_Request $request )

Filters WP_Query arguments when querying posts via the REST API.

Changelog

VersionDescription
4.7.0Introduced.

User Contributed Notes

You must log in before being able to contribute a note or feedback.