How to filter for multiple custom field key / values

Rideshare Custom Fields captured via More Fields
Rideshare Custom Fields captured via More Fields

As I mentioned at the end of my last post on the Power of Custom Fields, I recently started working on a prototype site that required rideshare board functionality. Storing the details lends itself well to custom fields, especially when More Fields is used for data capture as you can see from the inset image. However, I struggled to find a solution within WordPress to solve a display challenge: How to display a set of posts based on multiple custom field values? In my case, how to loop through rideshares base on event and ride-givers or ride-wanters.

I explored looping through each post in my rideshare category to match the rideshare event to an array value and then using get_post_meta within to see if results belonged to the ride-giver or ride-wanter. While this might have worked, it certainly wouldn’t be an elegant – or Idealien solution. Dan Butcher did provide a functional solution in the WordPress Support Forums for a similar challenge, but it is not a solution a novice / intermediate WordPress developer would be comfortable customizing, nor would it scale well. Barry @ clearskys.net informed me that,

Whilst the JOINS work, and will work quite well with a few meta-data criteria passed in, I’ve had problems (particularly on shared hosting) in the past when creating an SQL query to search based on a series of tags which used a similar method.

Introducing get_post_meta_multiple

This function I created, with sql optomization support from Barry @ clearskys.net:

  • accepts a variable length array of custom field key/value pairs
  • returns a list of post IDs which you can loop through
  • Could be implemented into your site in one of 3 ways: Added to a functions.php file as a part of your theme, included in a plugin related to enhanced custom field functionality or report a (feature request) bug for a more evolved version of it be included in a future release of the core of WordPress.

The demonstration code below has been prepared for the most common scenario as part of a functions.php file in a theme.

The Demonstration

Custom Field Table
A series of tables with columns for number of seats available, departure / return dates plus contact / additional info. I have kept the code below intentionally short so the example doesn’t overwhelm. I have posted a more complete version on pastebin that has the markup to match the image above.

The function

function get_post_meta_multiple($metaDataList) {
	global $wpdb;
	
	$querystr = "SELECT p.* FROM $wpdb->posts AS p WHERE p.ID IN ( ";
	
	$querystr .= "SELECT post_id FROM $wpdb->postmeta WHERE ";
	$innerqry = array();
	foreach($metaDataList as $key => $value) {
		$innerqry[] = $wpdb->prepare( "(meta_key = %s AND meta_value = %s)", $key, $value );	
	}
	
	$querystr .= implode(" OR ", $innerqry);
	
	$querystr .= " GROUP BY post_id ";
	$querystr .= "HAVING count(*) = " . count($metaDataList);
	
	$querystr .= ") AND p.post_status = 'publish' ";
	
	$metaResults = $wpdb->get_results($querystr, OBJECT);					
	return $metaResults;
}

Example Usage

Place this inside your category.php or whatever template file you are using to display the results from.

 'Ottawa Blues Fest',
	'rideshare_type'	=> 'Driver'); 
					
	$resultList = get_post_meta_multiple($metaDataList);

	if ($resultList):
	//Loop through each result post to display appropriate contents
	foreach ($resultList as $post):
		setup_postdata($post);
		
		// Define your custom field key
		$key="rideshare_spaces";
		
		// Display value of custom field		
		echo get_post_meta($post->ID, $key, true);

   	endforeach;
	else : ?>
		

The future of the function?

Once I have finished the prototype site that started this concept, I will attempt to package up the pieces in a more manageable method to share for wider use. In the interim, feel free to use and share in whatever ways are most convenience for you. Much like WordPress as a whole, I think this idea can continue to evolve in some great ways that will make it easier for designers, developers and clients to build / use sites based on it. Please link back to this post as you do so that I can keep tabs on how this idea evolves.

Similar Posts

13 Comments

  1. Wow, fantastic start. This is very close to the solution I have been searching for all day. Can’t wait to see how it evolves.

  2. Thanks for this, I have been struggling with a solution to this sort of problem. As web designer foremost and only code solutions as a last resort, this sort of help is fantastic.

    Although i am using Custom Field Search plugin, i will try and adapt it and integrate this code if possible.

    In one short google search i find this site and the links you provide to be exactly what i am after. Much appreciated.

  3. This function is excellent – thanks.

    Unfortunately I’m having difficulty customising the ORDER BY in the query.

    I need to order my query by menu_order and date (as below), but unfortunately this causes the query to return no results. I’m not sure if this is because some extra table rows need to be queried?

    ORDER BY = 'menu_order date'

    Thanks in advance

      1. I did it with

        $resultList = get_posts(‘orderby=meta_value_num&meta_key=total_property_size&order=ASC’);

        thank you so much. For your perfect code.

        Now I just need pagination.

        I found

        # put limit in query built above

        $ppp = intval(2);

        if(!$isPagination) :

        $on_page = intval(get_query_var(‘paged’));

        if($on_page == 0){ $on_page = 1; }

        $offset = ($on_page-1) * $ppp;

        $querystr .= ” LIMIT $offset,$ppp”;

        endif;

        $metaResults = $wpdb->get_results($querystr, OBJECT);

        if($isPagination) :

        $wp_query->found_posts = count($metaResults);

        $wp_query->max_num_pages = ceil($wp_query->found_posts / $ppp);

        $on_page = intval(get_query_var(‘paged’));

        if($on_page == 0){ $on_page = 1; }

        $offset = ($on_page-1) * $ppp;

        endif;

        return $metaResults;

        this code for pagination.

        But could not implement it.

        Could you help.

  4. Hi Jamie, amazing function you have there!

    But I have a problem, the function compares the exact value of the custom field and returns no result if values are not the same. How can I modify it so it still finds/display the value no matter what texts it follow? I tried modifying the sql query but in vain.

    Thanks.

    1. Thanks Mike. You should try an sql wildcard %. You might have to do more than just modify the $metaDataList key / value you are passing in to get the type of results you expect.

      Good luck!
      Jamie

  5. Great post! How would I use this in conjunction with searching a categories list? I would like to filter on multiple custom fields as well as the default post categories. Any help would be greatly appreciated!

    Thanks for the great function!
    Daniel

Comments are closed.