Support

Account

Home Forums Add-ons Repeater Field ACF 5.0+ Dynamically Populating Repeater

Solved

ACF 5.0+ Dynamically Populating Repeater

  • I’ve been reading a ton of topics regarding populating the repeater field and they all say the same thing: “Use acf/load_Value hook and return a multidimensional array of values keyed by subfield keys”. Which I have an example of below:

    Array
    (
        [0] => Array
            (
                [field_5b69f119ce5d2] => Name Here
                [field_5b69f1a8ce5d4] => Address Here
            )
    )

    Whenever I run mock tests, the filter hook expects an integer ( number of subfields ) to be returned. Then all the subfields are run through the same acf/load_value hook but there’s nothing to discern them from regular fields besides the concatenated name. Are the old ways supposed to work in 5.0+? With acf-json? Open to any tips or suggestions on how I can get his to work.

    What I’m trying to do is load values from a custom table into the repeater field. Assuming I have an array as seen above, how to I populate the repeater field as such? I’m using acf-json to store my fields. Here’s the function I’m working with:

    add_filter( 'acf/load_value', function populate_custom_table_data( $value, $post_id, $field ) {
    
    	if( ! empty ( $value ) && ! empty( $field['custom-attribute-here'] ) ) {
    		return $value;
    	}
    
    	$table_util = new Custom_Table_Utility();
    	$value 		= $table_util->get_data( $post_id, $field['name'] );
    	$value 		= ( empty( $value ) && 'true_false' !== $field['type'] ) ? '' : $value;
    
    	// Replace reapter field name keys with actual field keys
    	if( 'repeater' == $field['type'] && ! empty( $value ) ) {
    
    		/*
    		*	$value = Array
    		*	(
    		*		[0] => Array
    		*			(
    		*				[database_column_name] => Name Here
    		*				[database_col_address] => Address Here
    		*			)
    		*	)
    		*/
    
    		$subfield_keys = wp_list_pluck( $field['sub_fields'], 'key' );
    		
    		if( count( $subfield_keys ) == count( $value[0] ) ) {
    
    			foreach( $value as $key => $row_values ) {
    				$value[ $key ] = array_combine( $subfield_keys, $row_values );
    			}
    
    			/*
    			*	$value = Array
    			*	(
    			*		[0] => Array
    			*			(
    			*				[field_5b69f119ce5d2] => Name Here
    			*				[field_5b69f1a8ce5d4] => Address Here
    			*			)
    			*	)
    			*/
    
    		}
    
    	}
    
    	return $value;
    
    }, 10, 3 );

    I’ve also tried to populate it into the sub_fields index using the acf/load_field hook to no avail. With the acf/load_field hook I tried skipping repeater fields entirely and returning the row count but both shows the same results. I think I’m too early at this hook so it’s ignoring the sub_fields.

  • The solution is a bit messy and filled with a bunch of my classes functionality that won’t make much sense.

    The gist is that:

    • Repeaters need an integer count of expected subfields returned as a value from this hook.
    • Subfields names are prefixed with the parent repeater name.

    What I did was check the field type against ‘repeater’ and returned my custom database value count. At the top of the hook I checked if the $field[‘parent’] was empty, if not I used get_field_object( $field['parent'], $post_id, false, false ) to get the parent field name that the subfield are prefixed with.

    Once I returned the count to the repeater and stripped the parent name prefix off the subfields I was able to prepopulate the repeater with my custom table data. I have no idea if this will be helpful to anyone but at least it’s no longer a niche issue on the forums here.

  • Thanks for posting the solution – even returning an integer has moved me forward a bit.

    I know you are saying your code is full of red herrings relating to your plugin, could you post some of it anyway? – I can’t workout what to do based on this;

    stripped the parent name prefix off the subfields I was able to prepopulate the repeater with my custom table data

    Tantalising. Sorry to be a pain asking for the codez !

    Robbie

  • @mcnab It looks like this:

    /**
     * Load value from custom table
     * 
     * @param Mixed $value
     * @param Integer $post_id
     * @param Array $field
     * 
     * @return $value
     */
    function populate_custom_table_data( $value, $post_id, $field ) {
    
    	if( ! empty( $value ) ) {
    		return $value;
    	}
    
    	$has_parent_field	= false;
    	$field_name 		= $field['name'];
    
    	/**
    	 * Grab parent field and get value by the parent field name
    	 * Use conditional below to populate the actual fields.
    	 */
    	if( ! empty( $field['parent'] ) && false !== strpos( $field['parent'], 'field_' ) ) {
    
    		// false, false is important to prevent infinite recursive loop.
    		$parent_field 	  = get_field_object( $field['parent'], $post_id, false, false );
    		$has_parent_field = ( ! empty( $parent_field ) );
    		$field_name 	  = ( $has_parent_field ) ? $parent_field['name'] : $field_name;
    
    	}
    	
    	// Grab data from custom table
    	$db_data   	= $this->data_util->get_data( $post_id, $field_name );
    	$value		= $db_data;
    
    	// Return early if value is empty
    	if( empty( $value ) ) {
    		return $value;
    	}
    
    	// We're in a subfield
    	if( $has_parent_field ) {
    
    		$field_concat_name = $field['name'];
    		$field_concat_name = str_replace( sprintf( '%1$s_', $field_name ), '', $field_concat_name );
    		preg_match( '(\d{1,})', $field_concat_name, $possible_keys );
    
    		if( ! empty( $possible_keys ) ) {
    
    			$arr_index 			= $possible_keys[0];
    			$field_concat_name 	= str_replace( sprintf( '%1$d_', $arr_index ), '', $field_concat_name );
    
    			// Now we know what index in our subarray
    			$subfield_arr		= $db_data[ $arr_index ];
    
    			// Overwrite $value to actual subfield value
    			// Now that we know the name we can grab it from our named index array
    			$value = ( ! empty( $subfield_arr[ $field_concat_name ] ) ) ? $subfield_arr[ $field_concat_name ] : '';
    		}
    		
    
    	} else if( 'repeater' == $field['type'] ) {	// Return repeater count
    
    		$value = count( $db_data );
    
    	}
    
    	return $value;
    
    }
    add_filter( 'acf/load_value', 'populate_custom_table_data', 20, 3 );
  • @howdy_mcgee – you are an absolute legend.

    Thanks a million for coming back with this, it’s a huge help. Will crack on with this and reply to add anything I can to the thread for future users.

    Cheers,

    Robbie

  • I was running ACF PRO 5.7.0 but as an mu-plugin so it wasn’t obvious to me there was newer versions.

    Elliot replied in a ticket to me;

    In the most recent versions of ACF and ACF PRO we have moved around the order of actions allowing the “acf/load_value” action to run last (after “acf/load_value/type=repeater”).

    If you hook into the “acf/load_value” filter, you should now be able to customize the repeater field value as an array, not as a numeric number.

    So this has changed very recently. Now with version 5.7.6 you can handle it like this;

    	function my_acf_load_method($value, $post_id, $field) {
    			
    		if ($field['type'] == 'number') {
    
    			$value = 123;			
    
    			return $value;
    
    		} elseif ($field['type'] == 'repeater') {	
    		
    			$value[] = array(
    				'field_url' => 'http://www.google.com',
    				'field_text' => 'Google'
    			);
    
    			return $value;
    			
    		}	
    		
    	}
    	
    	add_filter('acf/load_value', 'my_acf_load_method', 20, 3);
    

    Checking the value of your repeater, returning $value as a string where the acf/load_value method is expecting a string, and $value as an array when it’s expecting an array.

    Much easier, thanks again Howdie_McGee.

Viewing 6 posts - 1 through 6 (of 6 total)

You must be logged in to reply to this topic.