Query posts by custom fields

Overview

This article will demonstrate how to retrieve an array of post objects from the database using native WP functions. There are many ways to query posts in WP, however, this article will make use of the common get_posts function, WP_Query Object and pre_get_posts filter.

Getting started

If you are already familiar with the above function, object and filter you may skip this section.

The WP_Query object is used to query posts and will return an object containing an array of $post objects and many useful methods.

The get_posts function makes use of the above WP_Query object, however, it only returns an array of $post objects making it a simpler way to find and loop over posts.

The pre_get_post filter is called after the query object is created, but before the actual query is run.

Example

This example demonstrates how to query all posts and display them in a list. Please note the functions setup_postdata() and wp_reset_postdata() are used to allow functions such as the_permalink() and the_title() to work as expected.

<?php 

$posts = get_posts(array(
	'posts_per_page'	=> -1,
	'post_type'			=> 'post'
));

if( $posts ): ?>
	
	<ul>
		
	<?php foreach( $posts as $post ): 
		
		setup_postdata( $post );
		
		?>
		<li>
			<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
		</li>
	
	<?php endforeach; ?>
	
	</ul>
	
	<?php wp_reset_postdata(); ?>

<?php endif; ?>

Custom field parameters

Both the get_posts function and WP_Query Object accept arguments to query custom field values. There is both a basic and advanced way to query which are explained below. You can read more about parameters over on the WP codex

Basic Example

This example shows the arguments to find all posts where a custom field called ‘color’ has a value of ‘red’.

$posts = get_posts(array(
	'numberposts'	=> -1,
	'post_type'		=> 'post',
	'meta_key'		=> 'color',
	'meta_value'	=> 'red'
));

Advanced Example

This example shows arguments to find all posts where a custom field called ‘color’ has a value of ‘red’ or ‘orange’ and another custom field called ‘featured’ (checkbox) is checked.

$posts = get_posts(array(
	'numberposts'	=> -1,
	'post_type'		=> 'post',
	'meta_query'	=> array(
		'relation'		=> 'AND',
		array(
			'key'	 	=> 'color',
			'value'	  	=> array('red', 'orange'),
			'compare' 	=> 'IN',
		),
		array(
			'key'	  	=> 'featured',
			'value'	  	=> '1',
			'compare' 	=> '=',
		),
	),
));

Examples

below you will find an assortment of examples. Please note these examples use the WP_Queryobject rather than the get_posts function, however the arguments and logic remain the same.

1. Single custom field value

In this example, we will find all posts that have a post_type of ‘event’ where the custom field ‘location’ is equal to ‘Melbourne’. The custom field ‘ location’ in this case could be a text field, radio button or select field (something that saves a single text value)

<?php 

// args
$args = array(
	'numberposts'	=> -1,
	'post_type'		=> 'event',
	'meta_key'		=> 'location',
	'meta_value'	=> 'Melbourne'
);


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

?>
<?php if( $the_query->have_posts() ): ?>
	<ul>
	<?php while( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<li>
			<a href="<?php the_permalink(); ?>">
				<img src="<?php the_field('event_thumbnail'); ?>" />
				<?php the_title(); ?>
			</a>
		</li>
	<?php endwhile; ?>
	</ul>
<?php endif; ?>

<?php wp_reset_query();	 // Restore global post data stomped by the_post(). ?>

2. Multiple custom field values (text based values)

In this example, we will find all posts that have a post_type of ‘event’ where the custom field ‘location’ is equal to ‘Melbourne’ and the custom field ‘attendees’ is higher than 100. The custom field ‘ attendees’ in this case could be a number field, text field, radio button or select field (something that saves a single text value)

<?php 

// args
$args = array(
	'numberposts'	=> -1,
	'post_type'		=> 'event',
	'meta_query'	=> array(
		'relation'		=> 'AND',
		array(
			'key'		=> 'location',
			'value'		=> 'Melbourne',
			'compare'	=> '='
		),
		array(
			'key'		=> 'attendees',
			'value'		=> 100,
			'type'		=> 'NUMERIC',
			'compare'	=> '>'
		)
	)
);


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

?>
<?php if( $the_query->have_posts() ): ?>
	<ul>
	<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<li>
			<a href="<?php the_permalink(); ?>">
				<img src="<?php the_field('event_thumbnail'); ?>" />
				<?php the_title(); ?>
			</a>
		</li>
	<?php endwhile; ?>
	</ul>
<?php endif; ?>

<?php wp_reset_query();	 // Restore global post data stomped by the_post(). ?>

3. Multiple custom field values (array based values)

In this example, we will find all posts that have a post_type of ‘event’ where the custom field ‘location’ is equal to ‘Melbourne’ or ‘Sydney’. The custom field ‘ location’ in this case could be a multi-select or checkbox field (something that saves a serialized array value)

<?php 

// args
$args = array(
	'numberposts'	=> -1,
	'post_type'		=> 'event',
	'meta_query'	=> array(
		'relation'		=> 'OR',
		array(
			'key'		=> 'location',
			'value'		=> 'Melbourne',
			'compare'	=> 'LIKE'
		),
		array(
			'key'		=> 'location',
			'value'		=> 'Sydney',
			'compare'	=> 'LIKE'
		)
	)
);


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

?>
<?php if( $the_query->have_posts() ): ?>
	<ul>
	<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<li>
			<a href="<?php the_permalink(); ?>">
				<img src="<?php the_field('event_thumbnail'); ?>" />
				<?php the_title(); ?>
			</a>
		</li>
	<?php endwhile; ?>
	</ul>
<?php endif; ?>

<?php wp_reset_query();	 // Restore global post data stomped by the_post(). ?>

4. Sub custom field values

In this example, we will find all events that have a ‘city’ or either ‘Melbourne’ or ‘Sydney’. Each ‘city’ is added as a new row to a repeater field called ‘location’.

To successfully query sub field values, we need to remember that the row number is not known (there may be 1,2 or even 3 rows of repeater field data). Therefore, we need to use a LIKE clause in our SQL query to allow for a WILDCARD in the meta_key search. To do this, we create a custom filter to replace the standard ‘=’ with ‘LIKE’. You can see this below.

<?php 

// filter
function my_posts_where( $where ) {
	
	$where = str_replace("meta_key = 'locations_%", "meta_key LIKE 'locations_%", $where);

	return $where;
}

add_filter('posts_where', 'my_posts_where');


// vars
$city = 'Melbourne';


// args
$args = array(
	'numberposts'	=> -1,
	'post_type'		=> 'event',
	'meta_query'	=> array(
		'relation'		=> 'OR',
		array(
			'key'		=> 'locations_%_city',
			'compare'	=> '=',
			'value'		=> 'Melbourne',
		),
		array(
			'key'		=> 'locations_%_city',
			'compare'	=> '=',
			'value'		=> 'Sydney',
		)
	)
);


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

?>
<?php if( $the_query->have_posts() ): ?>
	<ul>
	<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
		<li>
			<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
		</li>
	<?php endwhile; ?>
	</ul>
<?php endif; ?>

<?php wp_reset_query();	 // Restore global post data stomped by the_post(). ?>

Dynamic $_GET parameters

This example shows how to use $_GET parameters (from the URL) to modify the query of a post type archive. This example assumes a post type exists for ‘event’ and that it’s archive exists at the url; www.website.com/events.

The event post type contains a select field called ‘city’ with values such as ‘melbourne’ and ‘sydney’. By adding a parameter to the url, the query will be modified and only posts that match the ‘city’ will be shown; www.website.com/events?city=melbourne.

functions.php

function my_pre_get_posts( $query ) {
	
	// do not modify queries in the admin
	if( is_admin() ) {
		
		return $query;
		
	}
	
	
	// only modify queries for 'event' post type
	if( isset($query->query_vars['post_type']) && $query->query_vars['post_type'] == 'event' ) {
		
		// allow the url to alter the query
		if( isset($_GET['city']) ) {
			
    		$query->set('meta_key', 'city');
			$query->set('meta_value', $_GET['city']);
			
    	} 
		
	}
	
	
	// return
	return $query;

}

add_action('pre_get_posts', 'my_pre_get_posts');

Related