ACF - How To Display All Fields In A Field Group

As a WordPress developer, I create a lot of custom data to give clients the functionality they want. I’ve used every way possible, like posts for a custom post type, post meta, taxonomies, options, custom tables, etc. However, for ease of use and client convenience, I mostly use either WordPress native custom fields or the Advanced Custom Fields PRO plugin. With ACF, the interface is great, and, having used it so much, I can quickly setup any field type very quickly. The most time consuming part of using custom data is when it comes time to integrate into the theme.

For Example

One use case I keep encountering is displaying separate fields in a single list. Think of product product specifications. We might have fields like UPC, Length, Weight, Material, Pages, and so on. If we’re using a plugin like ACF, our specifications fields might use various field types may vary as well. However, I like to give specifications their own field group, separating them from content that may be displayed more free-form on the public side.

With predictable display characteristics on the front end, we can setup a UL to display the values of the specs. However, we have to know the field name for each specification we want to display and call the_field to spit it out. Needless to say, if we have a ton of specification fields, we’ll be violating the “DRY”1”DRY” = “Don’t Repeat Yourself”. best practice as a developer. It would be nice if we could condense the code for getting and displaying our fields.

Get All Field Group Fields

It turns out that, with a bit of custom code and a function provided by ACF, we can elegantly solve our predicament. The solution I’m providing here only works with ACF, but could be modified to work with standard post meta or posts of a custom post type in WordPress. We’ll start by getting the fields and aggregating them.

The following assumes you have created fields in a field group using ACF. Open up your theme’s functions.php file, and add the following:

<?php
function get_specifications_fields() {

	global $post;
	
	$specifications_group_id = 479; // Post ID of the specifications field group.
	$specifications_fields = array();
	
	$fields = acf_get_fields( $specifications_group_id );
	
	foreach ( $fields as $field ) {
		$field_value = get_field( $field['name'] );
		
		if ( $field_value && !empty( $field_value ) ) {
			$specifications_fields[$field['name']] = $field;
			$specifications_fields[$field['name']]['value'] = $field_value;
		}
	}
	
	return $specifications_fields;

}

This function get all fields for a given field group using the acf_get_fields function from ACF. The only variable is on line 5, which will hold the field group ID.2You can see this in the URL when editing a field group. While looping through all the fields in the group, we check for a value. If a field has no value, we ignore it. Fields with valid values are stored in a separate array that is eventually returned.

Now we need to use this code to display our specifications in the theme. Pick a place to display the data, and use something like this:

<div class="row">  
<?php 
    $specifications_fields = get_specifications_fields();
              
    foreach ( $specifications_fields as $name => $field ):
        $value = $field['value'];
?>     
     
    <div class="col-xl-3 col-lg-4 col-md-4 col-sm-6 col-xs-12 key">
        <strong><?php echo $field['label']; ?>:</strong>
    </div>
    <div class="col-xl-9 col-lg-8 col-md-8 col-sm-6 col-xs-12 value">
        <?php echo $value; ?>      
    </div> 
         
    <?php endforeach; ?>
</div> 

For simplicity, I’m using some Bootstrap classes to help style our specs as a list. The rest of the code is pretty straightforward. We’re calling our get_specifications_fields function and looping through the results.

Other Possibilities

Although this example is very simple, it illustrates how to condense repetitive code. I’ve used this code in custom plugins. I usually add filters for the field data at various points to exclude fields and alter the results. I also have code to do certain things based on a specific field’s type. For instance, a true-false field may be converted to a “Yes” or “No” when returned.

Finally, when I use this code in a plugin, I usually create a shortcode for the admin/editor use inside their main content, whether that’s the_content, a page builder, ACF Flexible Content, or tons of other places. With the right UI, the user can select which field group to use and the ID is passed as a parameter to the shortcode. The downside of using this as a plugin is that the structure/styling of the output is a little less accessible because it’s no longer part of the theme. However, we deal with that all the time for many other WordPress plugins.

About

Web Developer

References

References
 1 ”DRY” = “Don’t Repeat Yourself”.
 2 You can see this in the URL when editing a field group.

29 Comments

  1. Bree

    This is awesome, thank you. Can you tell me how you might echo values from something like a checkbox when it may be outputting an array?

    1. David

      You’re welcome. If you had a checkbox with multiple selected values, you would just loop through the array provided by get_field, like so:

      <?php
      $checkbox_values = get_field('checkbox_values');
      
      if( $checkbox_values ): ?>
      <ul>
      	<?php foreach( $checkbox_values as $value ): ?>
      		<li><?php echo $value; ?></li>
      	<?php endforeach; ?>
      </ul>
      <?php endif; ?>
      1. Edu

        I use this code and its very useful but i have some question.

        1-how to add commas in this code.

        2-its make a url which is good, if i got space in text, it shows %20 in url. So how to convert this %20 into dash (-).

        3-url give capital word if i use capital word, is it possible that it will automatically creat small alphabets in url?

        Thanks again for your help.

        1. David
          1. You can mix the commas in with the HTML
          2. You can do rawurldecode the str_replace the decoded spaces with dashes. Be sure to filter_var and sanitize your URLs
          3. You could use the standard strtolower function to lowercase the entire string

          Hope this helps!

  2. Jansie

    Thank you a hundred times over. I modified your code to suit one of my applications and it works a charm.

    I asked at the ACF forum and I’m still hearing crickets, so this is a site-saver.

    So chuffed. Thanks again.

  3. alex

    Thanks a lot for this,
    how about if the field group contain a bunch of repeater fields?
    Any way we can output the single values rather than ‘Array’?

    1. David

      You’re welcome!

      In the case of a “repeater” field type inside the group, you could refactor the code to get the individual child fields of the parent. So, the get_specifications_fields function would accept the post ID of the field group as an argument, and it would just return an array. Another function would flatten the resultant fields into your returned array.

      I’ll post a proof of concept soon!

      1. alex

        Hi David,

        Any chance you might have sample code to get this working with repeaters?
        I’ve been unable to do so and going crazy!

        Cheers

    1. David

      When using the “User” relationship field type, the “value” result from calling acf_get_fields will be an array. The structure depends on whether or not you allow multiple users to be selected for the field. In either case, the end value for the user is an array contains data similar to WP_User.

      Using the example code above, you have a couple of options. Depending on what data you need to display for the user, you might flatten the returned data include only what you need. For example,

      		if ( $field_value && !empty( $field_value ) ) {
      			$specifications_fields[$field['name']] = $field;
      			$specifications_fields[$field['name']]['value'] = $field_value['display_name'];  // You could also use keys like ID, user_firstname, etc.
      		}
      

      However, this edit would only work for user field types and breaks functionality for others. I would actually return the original value in your $specifications_fields array, leaving the original get_specifications_fields function unchanged.

      Instead, you could just change what is displayed in the theme. Looking at the example theme code above, you could check the field type, and display the correct “value” accordingly.

          <div class="col-xl-9 col-lg-8 col-md-8 col-sm-6 col-xs-12 value">
              <?php if ( $field['type'] === 'user' ) : ?>
                  <!-- Again, you can use any of the keys provided by the return value, like ID, user_firstname, etc. //-->
                  <?php echo $value['display_name']; ?> 
              <?php else : ?>
                  <?php echo $value; ?>
              <?php endif; ?>
          </div> 
      

      In this example, you could also conditionally check the field $name or any of the other data keys being returned. Just debug out what your get_specifications_fields function returns, using print_r().

    1. David

      In ACF, field groups are actually just posts with a post type of “acf-field-group.” Using the post ID of a field group, you can just call WordPress’s get_the_title function. Example:

      <h1><?php echo get_the_title( 479 ); ?></h1>

      If you need to show multiple lists using multiple field groups, you can refactor the above code to use an array of post IDs. You can do this in many different ways, but here’s one example:

      <?php
      function get_specifications_fields() {
      
          global $post;	
      
          $specifications_groups = array( 479, 480, 481 ); // Post IDs of the specifications field groups.
          $specifications_fields = array();
      	
          foreach ( $specifications_groups as $group_post_id ) {
              $fields = acf_get_fields( $group_post_id );
      
              // Prefix field name with the group post ID to prevent key conflicts
              $field_group_key = $group_post_id.'-'.$field['name'];
      	
              foreach ( $fields as $field ) {
                  $field_value = get_field( $field['name'] );
      		
                  if ( $field_value && !empty( $field_value ) ) {
                      $specifications_fields[$field_group_key] = $field;
                      $specifications_fields[$field_group_key]['value'] = $field_value;
                  }
              }
          }	
      	
          return $specifications_fields;
      
      }
      

      This code just loops through a list of ACF field group post IDs. Another change is the key for each field is now prefixed by the current iteration’s group post ID. This will prevent conflicts when two fields in separate groups have the same name.

  4. Moses

    Hi, how do I display a group of fields related to the current term/taxonomy of the page?
    I tried:
    global $wpdb;
    $specifications_group_id = $term_id;

    but it did not work

  5. M-W

    Was there ever a sample posted with how to loop through repeaters? My text fields show up fine but I’m having a hard time drilling down into sub-fields. It just says “array”.

    1. David

      Here are a few suggestions on how to handle the Repeater field type. I’ve componentized a few functions for reusability and nestability. You’ll want to add this after the get_specifications_fields function which remains unchanged.

      <?php
      function output_specification_text_field( $field = array() ) {
      	if ( is_array( $field['value'] ) ) return output_specification_array( $field['value'] );
      ?>   
      		 
      	<div class="col-xl-3 col-lg-4 col-md-4 col-sm-6 col-xs-12 key">
      		<strong><?php esc_html_e( $field['label'] ); ?>:</strong>
      	</div>
      	<div class="col-xl-9 col-lg-8 col-md-8 col-sm-6 col-xs-12 value">
      		<?php esc_html_e( $field['value'] ); ?>      
      	</div>  
      
      <?php
      }
      
      function output_specification_image_field( $image = array() ) {
      ?>   
      		 
      	<div class="col-xs-12 image">
      		<?php esc_html_e( wp_get_attachment_image( $image['ID'], 'full' ); ?>      
      	</div> 
      
      <?php
      }
      
      function output_specification_array( $subfields = array() ) {
      	if ( sizeof( $subfields ) < 1 ) return;
      
          foreach ( $subfields as $subfield ) {
      		foreach ( $subfield as $key => $value ) {
      			switch ( $key ) {
      				case 'repeater':
      					output_specification_array( $value );
      					break;
      				case 'image':
      					output_specification_image_field( $value );
      					break;
      				case 'text':
      					output_specification_text_field( array(
      						'label' => $key,
      						'value' => $value,
      					) );
      					break;
      			}
      		}
          }
      }
      ?>

      This code is only a demonstration for Repeater subfield types of ‘repeater’, ‘image’, and ‘text’. It will have to be customized to work with your specific subfields by adding new switch cases inside the output_specification_array function.

      In your theme template file where you want to output the markup created by these functions, you can simply replace the code referenced in the tutorial with:

      <?php
      <div class="row">
              <?php output_specifications_fields(); ?>
      </div> 
      ?>

      Hope this helps!

    1. Shyamol Ghosh

      This code just loops through a list of ACF field group post IDs. Another change is the key for each field is now prefixed by the current iteration’s group post ID. This will prevent conflicts when two fields in separate groups have the same name.

      1. David

        Yes, if you have two field groups with the name name, you can concatenate the name with the ID to ensure they are unique. Example:

        <php
        foreach ( $fields as $field ) {
        	$field_value = get_field( $field['name'] );
        		
        	if ( $field_value && !empty( $field_value ) ) {
        		$field_key = sprintf(
        			'%s-%s',
        			$field['name'],
        			$field['ID']
        		);
        
        		$specifications_fields[ $field_key ] = $field;
        		$specifications_fields[ $field_key ]['value'] = $field_value;
        	}
        }
        ?>
        
  6. Narga

    Hello,
    You saved me a lot of time. I tried to modify your code but it’s not work.
    I have 3 group of custom field: Group A, Group B, Group C. I tried to get the values each groups to separated array like $group1[value], $group2[value] and $group3[value].
    Can you give me an example?
    Thanks

    1. David

      I would just grab all three field groups and concatenate them into one array with the group being a disparate key. So, something like:

      <php
      $groups = array(
          'groupA' => $groupA_fields,
          'groupB' => $groupB_fields,
          'groupC' => $groupC_fields,
      );
      ?>
      

      Then, you can loop through them in a similar fashion as described in the post, but you’ll have to add another, higher-level loop.

  7. Matthew Wood

    Hi, thank you. It’s the only thing close to what I need that I could find. I wonder is there away to do this with also data from a specific page. I have a contact page with two groups of fields. One with social links and one with address info. I want to group all the information from the address field from the contact page to place on the footer of each page. The code example looks like its for dynamic pages, i.e. page.php or whatever. But I just want the data from one group from one specific page. So I’m trying to figure out how to tell it to include $page_id

  8. Tomáš

    Thank you for this tutorial. I’m trying it in Oxygen Builder and putting everything inside the code block. But it doesn’t work 🙁 Even when I use only part of the code and checking the functionality with echo or print_r I’m not able to see values.

    1. Tomáš

      I’m trying to put the echo here

      foreach ( $specifications_fields as $name => $field ):
      $value = $field[‘value’];
      echo $field;
      ?>

      nothing is displayed

Add Your Thoughts

Your email address will not be published. Required fields are marked *