Support

Account

Home Forums Add-ons Repeater Field Get row index inside add_filter( 'acf/load_field' )

Solved

Get row index inside add_filter( 'acf/load_field' )

  • Hi.
    Is there any way of getting the row index inside the load_field filter:
    add_filter( 'acf/load_field' ) )
    I’m using a repeater field and I’m trying to pre-populate the subfield, based on the value of a sibling field, after that field value is saved to the database.

    How could I get the row_index the “current” for the field.
    get_user_meta( 2, 'product_data_' . $row_index . '_product', true ));

  • You cannot populate different rows of a repeater with different row values using an acf/load_field filter. The reason is that the sub field is only “loaded” once so the field will have the same values for all rows.

    This can be accomplished using the acf/prepare_field filter. I actually just did this yesterday 🙂

    In order to do this you need to keep track of the row index yourself. I did this by encapsulating the filter in a class. This is how I set up all my filters. Basically I set up a class where I add all my ACF filters. Here is a basic example of what you want to do.

    Also, note that when filtering sub fields of a repeater it is usually best to use the field key the field name will not be what you expect it to be. For example, the field name being used by ACF for my filter looks something like this

    
    acf[field_602be12e89017][row-2][field_602be2818901b][field_602be3658901c][row-0][field_602d11a1de2fd][row-0][field_602d10bd5cc06]
    

    the field that I am filtering is a sub field in repeater that is nested in another repeater that is then nested in a group field that is nested in another repeater. It may work with the name, but it is more likely that a sub field will share it’s name with a sub field of other repeaters. For example, the name of this field is “value” and I use this sub field name often. Anyway, on to the example.

    
    class MY_PROJECT_NAME_acf_filters {
      
      // variable for row index
      private $my_field_name_index = 0;
      
      public function __construct() {
        // add a filter when preparing repeater
        // I also tend to always use field keys when coding
        add_filter('acf/prepare_field/key=field_XXXXX', array($this, 'init_my_field_name_index'));
        
        // add filter for ths sub field that you want to filter
        add_filter('acf/prepeare_field/key=field_YYYYY', array($this, 'filter_my_sub_field_name'));
      }
      
      public function init_my_field_name_index($field) {
        $this->my_field_name_index = 0;
        return $field;
      }
      
      public function filter_my_sub_field_name($field) {
        $row_index = $this->my_field_name_index;
        
        // do your filtering for this row index
        
        // after filtering increment the field index
        $this->my_field_name_index++;
        
        // return the field;
        return $field;
        
      }
      
    }
    
  • Thank you so much @hube2! 🙂 I really appreciate your feedback and for sharing your code. Here is what I ended up using. I kept your naming convention for convenience.

    
    class MY_PROJECT_NAME_acf_filters {
    	  
    	// variable for row index
    	private $my_field_name_index = 0;
    	
    	public function __construct() {
    		// add a filter when preparing repeater
    		add_filter( 'acf/prepare_field/key=field_601cf9424f9da', array( $this, 'init_my_field_name_index' ), 10, 1 );
    		
    		add_filter( 'acf/prepare_field/key=field_6023ad409d89d', array( $this, 'filter_my_sub_field_name' ), 10, 1 );
    		
    		// add filter for ths sub field that you want to filter
    		add_filter( 'acf/prepare_field/key=field_602e8a8821e89', array( $this, 'filter_my_sub_sub_field_name' ), 10, 1 );
    	}
    	  
    	public function init_my_field_name_index( $field ) {
    		$this->my_field_name_index = 0;
    		return $field;
    	}
    	
    	public function filter_my_sub_field_name( $field ) {
    		// after filtering increment the field index
    		$this->my_field_name_index++;
    		
    		// return the field;
    		return $field;
    	}
    	  
    	public function filter_my_sub_sub_field_name( $field ) {
    		$field['choices'] = [];
    		
    		$row_index = $this->my_field_name_index - 2;
    		
    		$product_id = (int) get_user_meta( 2, 'add_products_' . $row_index . '_produkt', true );
    		
    		if ( ! empty( $product_id ) ) {
    			
    			$pewc_group = new WP_Query( [
    				'posts_per_page => ' -1,
    				'post_type' => 'pewc_group',
    				'post_parent' => $product_id,
    				'post_status' => 'publish'
    			] );
    			if ( $pewc_group->have_posts() ) {
    				while( $pewc_group->have_posts() ) { $pewc_group->the_post();
    					
    					$field['choices'] = [];
    					
    					$group_title = get_the_title();
    					$group_id = get_the_ID();
    					
    					$pewc_field = new WP_Query( [
    						'posts_per_page => ' -1,
    						'post_type' => 'pewc_field',
    						'post_parent' => get_the_ID(),
    						'post_status' => 'publish',
    						'order' => 'ASC'
    					] );
    					if ( $pewc_field->have_posts() ) {
    						while( $pewc_field->have_posts() ) { $pewc_field->the_post();
    							$field_label = get_post_meta( get_the_ID(), 'field_label', true );
    							if ( ! empty( $field_label ) ) {
    								$field['choices'][get_the_ID()] = $field_label . ' (' . get_the_ID() . ')';
    							}
    						}
    						wp_reset_postdata();
    					}
    					
    				}
    				wp_reset_postdata();
    			}
    		}
    
    		// return the field;
    		return $field;
    	}
    	  
    }
    new MY_PROJECT_NAME_acf_filters();
    
  • I had a similar issue where i needed to add the ACF fields meta_id. Below is my solution to the same problem

    function build_meta_key($field){
        // Loop over parents.
        $return_name = $field["_name"];
    
        if(empty($field["prefix"])){
            return $return_name;
        }
    
        $is_row = preg_match("/row-(\d)+/", $field["prefix"], $matches);
    
        if(!$is_row){
            return $return_name;
        }
    
        //now figure out the row number from the field name
    
        $return_name = $matches[1]."_".$return_name;
        
        while ( $field['parent'] && $field = acf_get_field( $field['parent'] ) ) {
            if(isset($field["_name"])){
                $return_name = $field["_name"]."_".$return_name;
            }
        }
    
        return $return_name;
    }
    
    function get_all_meta_info_by_key($post_id,  $meta_key, $meta_type = "post"){
    	global $wpdb;
    	if ( ! $meta_type || ! $post_id || trim($meta_key) == "") {
    		return false;
    	}
    
    	$table = _get_meta_table( $meta_type );
    	if ( ! $table ) {
    		return false;
    	}
    
    	$meta = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$table} WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key) );
    
    	if ( empty( $meta ) ) {
    		return false;
    	}
    
    	if ( isset( $meta->meta_value ) ) {
    		$meta->meta_value = maybe_unserialize( $meta->meta_value );
    	}
    
    	return $meta;
    }
    
    /**
     * Add in the ACF fields meta id to the field itself. so we can build the download links
     * @param $field
     *
     * @return mixed
     */
    function prepare_field( $field ) {
        global $post;
    
        $acf_meta_key = $this->build_meta_key($field);
    
        $meta_information = $this->get_all_meta_info_by_key($post->ID,  $acf_meta_key);
    
        if(!empty($meta_information->meta_id)){
            $field["post_meta_id"] = $meta_information->meta_id;
        }
    
        return $field;
    }
  • This was really useful. Thanks John and Vayu. Readers watch out for a small typo in John’s original answer on line 12 where it says

    prepeare_field which should, of course be prepare_field.

    It had me stumped for a short while.

  • @hube2
    Hi John, That’s really useful, wish i had found this sooner it would saved me a day or so.

    I’m really interested in your filtering is a sub field in repeater that is nested in another repeater that is then nested in a group field that is nested in another repeater.

    I am trying and failing to update my sub fields of nested repeaters.

    I have the following options page, basically i want to create business events (repeater 1)

    Field Group: group_6380cf9026f49
    Repeater (1): field_6380d01f71839 (these are events)
    Group(1): field_645e4a88f718e
    Group(2): field_645e3a33aa018
    Repeater (2): field_646205bfe80ff
    Group (3): field_6462172e85e75
    Select field: field_646206abe8102 (these are Tips)

    The select field is grabbing a field group name from a field in Group (2): field_645e628bf1588 which pulls all the field name values as choices from another field group.

    I’ve used your class code solution and it fetches the group name fine from Group (2): field_645e628bf1588, i can see it’s pulling back the field group values for Select field (field_646206abe8102)

    But the sub repeater that allows people to select the Tips per event aren’t the correct fields for the event when either adding new tips or editing previous tips. The very first tip in the first event is fine, then they are all out of sync to the row index or don’t show at all.

    I think i need another index counter for the sub rows, but i am not quite sure how best to achieve this. Would love to know more on how you handle this? especially as i’m going crazy now

    
    class MY_PROJECT_NAME_acf_filters 
    {
    
        // variable for row index
        private $my_field_name_index = 0;
    
        public function __construct()
        {
            // add a filter when preparing repeater for monitoring index count
            // Field: event -> field_6380d01f71839
            add_filter('acf/prepare_field/key=field_6380d01f71839', array($this, 'init_my_field_name_index'));
    
            // add filter for ths sub field that you want to filter
            // FIeld: tips field_646206abe8102
            add_filter('acf/prepare_field/name=field_646206abe8102', array($this, 'filter_my_sub_field_name'));
        }
    
        public function init_my_field_name_index($field)
        {
            $this->my_field_name_index = 0;
            return $field;
        }
    
        public function filter_my_sub_field_name($field)
        {
                // Get the index count
                $row_index = $this->my_field_name_index;
    
                //acf[field_6380d01f71839][row-2][field_645e4a88f718e][field_645e3a33aa018][field_646205bfe80ff][acfcloneindex][field_6462172e85e75]" (overview group)
               
    
                // Get Group Key Name from Group(2): field_645e3a33aa018  -> field_645e628bf1588 
                $getGroupKey = get_option('events_judging_event_' . $row_index . '_all_settings_judging_settings_judging_field_group');
    
                if ($getGroupKey) {
                    $field['choices'] = [];
    
                    // Get all fields for the group
                    $choices = acf_get_fields($getGroupKey);
    
                    if (is_array($choices)) {
                        // loop through array and add to field 'choices'
                        foreach ($choices as $choice) {
                            //Set the select values
                            if ($choice['type'] != 'message') {
                                $field['choices'][$choice['key']] = $choice['label']; 
                            }
                        }
                    }
                }
    
                // after filtering increment the field index
                $this->my_field_name_index++;
                return $field;
            } else {
                return $field;
            }
        
    }
    new MY_PROJECT_NAME_acf_filters ();
    
  • Yes, the example is only for a single repeater, when nesting fields you would need to keep track of every iteration of every repeater. I’m not sure how you can do that. The indexes would need to be reset each time either of the repeaters starts a new iteration. This is beyond my ability to help here.

  • Hi John!
    Can you help me?
    I need to implement the following idea:
    It is necessary to go through all the values of the subfield of the repeating field and find the rows index where there is a certain value. And then apply disable for all fields of this rows. The problem is that the prepare filter works in a row across the fields depending on their location, and it is not clear how to restart the filter after the first pass. Please help with this question.

  • Thanks John, i did manage to track multiple iterations for multiple nested sub fields. Your class was my guide to be able to eventually achieve this, it took me many days to complete.

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

You must be logged in to reply to this topic.