How to get all custom fields from Advanced Custom Fields

tl;dr You can get all the fields stored in Advanced Custom Field with:

    $groups = apply_filters( 'acf/get_field_groups', array() );
    if ( is_array( $groups ) ) {
        foreach ( $groups as $group ) {
            $fields = apply_filters( 'acf/field_group/get_fields', array(), $group['id'] );
            return $fields;
        }
    }

For my The Events Calendar Shortcode Pro plugin, I wanted to add the ability to filter by a field added with Advanced Custom Fields. This way with a field named additional_data someone could use the following shortcode to only list events with the value “Test Data” in that field:

[ecs-list-events additional_data="Test Data"]

So I started looking for documentation on getting all field groups from Advanced Custom Fields and then getting all the fields in each group, but the documentation for getting the groups was a bit thin.

I thought you could do something like:

$groups = get_field_groups();

But Advanced Custom Field actually hides this function within a class:

class acf_field_group
{
	var $settings;
		
	/*
	*  __construct
	*
	*  @description: 
	*  @since 3.1.8
	*  @created: 23/06/12
	*/
	
	function __construct()
	{
		// actions
		add_action('admin_enqueue_scripts', array($this,'admin_enqueue_scripts'));
		
		
		// filters
		add_filter('acf/get_field_groups', array($this, 'get_field_groups'), 1, 1);
		add_filter('acf/field_group/get_fields', array($this, 'get_fields'), 5, 2);
		// ...
	}
	
	function get_field_groups( $array )
	{
		// cache
		$found = false;
		$cache = wp_cache_get( 'field_groups', 'acf', false, $found );
		
		if( $found )
		{
			return $cache;
		}
		
		
		// get acf's
		$posts = get_posts(array(
			'numberposts' 	=> -1,
			'post_type' 	=> 'acf',
			'orderby' 		=> 'menu_order title',
			'order' 		=> 'asc',
			'suppress_filters' => false,
		));

		// populate acfs
		if( $posts ){ foreach( $posts as $post ){
			
			 $array[] = array(
				'id' => $post->ID,
				'title' => $post->post_title,
				'menu_order' => $post->menu_order,
			);
			
		}}

		// set cache
		wp_cache_set( 'field_groups', $array, 'acf' );
				
		return $array;
	}

	// ...
}

new acf_field_group();

We can’t just call the get_field_groups function directly because the instance of the acf_field_group class isn’t available to us. We could create another instance with

new acf_field_group()

somewhere in our code, but then we have two copies of the class running for no reason (and might mess things up with the filters being registered twice).

Unlike a normal filter which would take some data and either return it back or modify it, the get_field_groups function is ignoring the input value of $array and just returning the data for the field groups no matter what.

So you’re actually applying a filter to fetch the data, which isn’t really filtering anything:

$groups = apply_filters( 'acf/get_field_groups', array() );

Whereas normally the data would be fetched first into $groups and then apply_filters called so others can modify it, ie.:

$groups = ... some code to get the groups data ...;
$groups = apply_filters( 'acf/get_field_groups', $groups );

We can still hook into the acf/get_field_groups filter and modify the field groups after get_field_groups provides it, but the initial data isn’t available before the acf/get_field_groups filter is initially run.

Interesting usage of WordPress filters and not something I’ve seen done before!