Distinct values from ACF inputs


Including from a repeater field

Recently I used the ACF plugin to add some custom fields to a custom post type, including a repeater field. In my case, I was using these fields to get a country and state input for a client’s distributors. Due to the number of distributors that they have, we wanted to create a simple search form that lets a user select a country from a drop down to see a list of distributors in that country. The user could then filter further by selecting a state/province after selecting the country.

Getting a distinct list of countries from a regular ACF text field was simple, and could be done using the wpdb wordpress database class. In fact, you can use it to get distinct values for any post_meta, not just ones created through ACF.


function get_distinct_meta_values($meta_key){

	global $wpdb;

	$result = $wpdb->get_results($wpdb->prepare("
			SELECT DISTINCT meta_value
			FROM {$wpdb->postmeta}
			WHERE meta_key = '%s' AND meta_value != ''
			ORDER BY meta_value ASC
		", $meta_key
	));

	return $result;
}

The above function returns an array of objects that can be looped through to get the distinct values, which I was using to create a dropdown list of available countries.


<label>
	Country:
	<select name="country" id="country_selection">
		<option value="">- Select -</option>
		<?php
			//search through afc fields for unique countries, list them, boy
			$result = get_distinct_meta_values('country'); // country is the name of my ACF field here
			foreach ( $result as $val ) {
			   echo '<option value="'.htmlspecialchars($val->meta_value).'"';
			   if(isset($_GET['country']) && $_GET['country'] == $val->meta_value){
			   	echo ' selected="selected" ';
			   }
			   echo '>'.$val->meta_value.'</option>';
			}
		?>
	</select>
</label>

This was all good until I needed to add a repeater field for additional “secondary countries”. These needed to be included in the drop down list, so I created a function specifically for getting the countries from both a standard ACF field and the repeater field.


function get_country_list(){

	global $wpdb;

	$rows = $wpdb->get_results($wpdb->prepare( "
		SELECT DISTINCT meta_value
		FROM {$wpdb->postmeta}
		WHERE meta_key LIKE %s
			OR meta_key = 'country'
		ORDER BY meta_value ASC
		", 'also_services_%_country'
	));

	return $rows;
}

The author of the ACF plugin discusses how to query the database for repeater field values, and I adapted some of his sample code for this project.

I also needed to create a wordpress loop for the posts with a certain country in it’s country field, or it’s secondary country repeater field, and I accomplished that in two steps. Step one was to retrieve a list of distinct post IDs for the country being searched for, and I did that with the following function.


function get_post_ids_by_country($country){

	global $wpdb;

	$rows = $wpdb->get_results($wpdb->prepare("
		SELECT DISTINCT post_id
		FROM {$wpdb->postmeta}
		WHERE
			(meta_key LIKE %s
			OR meta_key = 'country')
		AND
			meta_value = '%s'
		", 'also_services_%_country',
		$country
	));

	//I needed an array of IDs for the query these would be used in
	//so I went ahead and pulled each ID out of the array of objects
	$list = array();
	foreach ($rows as $val) {
		$list[] = $val->post_id;
	}

  	return $list;
}

The above function returns an array of post IDs that have the searched for country assigned to it in either the regular ACF text field for the primary country, or in the secondary list of countries created through the repeater field. This array is then used in a custom wordpress query like so:


$country_array = get_post_ids_by_country($_GET['country']);

$args = array(
	'numberposts' => -1, // no limit to the number of posts returned (not always the best idea)
	'post_type' => 'distributor',
	'orderby' => 'ASC',
	'post__in' => $country_array
);

$the_query = new WP_Query($args);

if( $the_query->have_posts() ){

	while ( $the_query->have_posts() ) : $the_query->the_post();

	// the post layout is built here, but wasn't necessary to include.
}

While the code I’ve shared is specific to my needs for a project, it should provide a good idea of what can be done and should be easily adaptable for other uses.