RCNeil
RCNeil

Reputation: 8759

Add row to nested repeater in Wordpress Advanced Custom Fields PRO

I am running ACF PRO 5.3.9.2, but I think this pertains to any version with nested repeaters. I am hoping to add rows to a nested repeater (that is a repeater inside a repeater) using the update_field() or update_sub_field() function.

The update_field() function works great for first-level repeater rows, i.e.:

update_field( $field_key, $value, $postID );

but I am uncertain about how to use this for nested repeaters. This is the ACF Fields structure:

CUSTOM POST TYPE
    Repeater Field - "Media"
         Repeater Field - "Notes"

So, here is the code I am attempting:

$field_key = "field_xxxxxxxxxxxxx"; //NOTES FIELD KEY
$value = get_field($field_key, 12); //GET FIELDS OF POST ID 12, NOTES
$value[] = array("note" => "Here is a new note");
update_field( $field_key, $value, 12 );

But this does nothing. There is no way to determine WHICH "Media" repeater I am wishing to add a "note" to.

I can successfully "update" the nested repeater field using update_sub_field() like the code below IF and only IF there is a row in the nested repeater field, but it overwrites just the one row and cannot add to it. It also will not work if there isn't currently a row in the repeater.

update_sub_field( array('media', 1, 'notes', 2, 'note'), 'Here is a new note!', $postID );

What can I do to add rows to a nested repeater whether there is a row already or not?

Upvotes: 2

Views: 3594

Answers (4)

JShoop
JShoop

Reputation: 11

I fully realize this answer is 7 years late but I think it will be helpful for everyone attempting this. You don't need to create a custom function (maybe you did 7 years ago), ACF has functions to add and update sub fields (even nested repeaters).

To update a nested repeater use update_sub_field() as mentioned in the original post.

update_sub_field( array('media', 1, 'notes', 2, 'note'), 'Here is a new note!', $postID );

To add a new row use add_sub_row()

add_sub_row( array('media', 1, 'notes'), $note_vals, $postID );

$note_vals can be an array of fields if 'notes' has sub_fields or a value if not.

This took me waaaaaay too long to discover so I hope it helps you.

Upvotes: 1

23creative
23creative

Reputation: 21

What did end up working for me when i had a nested repeater was to use add_row with the parent's field name as a prefix. for example when i have nested field documents inside a field builtdocuments I can use

$sub_fields = array(
    'current_document'   => "encrypted_file"
);  
  
add_row( 'builtdocuments_document', $sub_fields); 

to add to the nested repeater successfully

Upvotes: 0

Elyas
Elyas

Reputation: 121

This is what I made for me, I hope it will be usefull for you guys.

I made a function that takes parameters and insert inside specific post (taked by id) a repeater with his own fields and inside this repeater insert another one or more than one (because it's a repeater) subrepeater with his own fields.

So if I have something like this:

Spoiler (Repeater)

------------ Nombre Spoiler (input)

------------ Spoiler Tag (input)

------------ Enlaces (Sub-Repeater)

------------------------- Nombre Enlace (input)

------------------------- Enlace (input)

with this function I can insert X "spoiler"s and x "Enlaces"s

here the function:

function insert_field_subfield($repeater_field, $repeater_subfield, $field_values, $subfield_values, $field_key, $postid){

if(get_field($field_key, $postid)){
    $value = get_field($field_key, $postid);
}else{
    $value = array();
}
$value[] = $field_values;

if(update_field( $field_key, $value, $postid)){

    $i = 0;

    if( have_rows($repeater_field, $postid) ){

        $spoiler_item = get_field($repeater_field, $postid);
        $total_rows = count(get_field($repeater_field, $postid)) -1;

        while( have_rows($repeater_field, $postid) ) : the_row(); 

            if($i == ($total_rows)){

                if( !is_array($spoiler_item[$i][$repeater_subfield]) ) {

                    $spoiler_item[$i][$repeater_subfield] = array();             
                }

                if (count($subfield_values) == count($subfield_values, COUNT_RECURSIVE)){ // subfield_values is not multidimensional

                    array_push($spoiler_item[$i][$repeater_subfield], $subfield_values);

                }else{ // subfield_values is multidimensional

                  foreach($subfield_values as $subfield_value){

                    array_push($spoiler_item[$i][$repeater_subfield], $subfield_value);

                  }

                } 
            }
            $i++;

        endwhile;

    if(update_field($field_key, $spoiler_item, $postid)){
        return true;
    }else{
        return false;
    }

}

}else{
    return false;
}}

To call:

$addField = array("nombre_spoiler" => "Otro nombre", "spoiler_tag" => "Spoiler tag");

For one value inside subrepeater:

$addSubField = array( 
'nombre_enlace' => 'Valor de un array no multidimensional',
'enlace' => 'Valor enlace');

For multiple values:

$addSubField[] = array( 
'nombre_enlace' => 'multidimensional',
'enlace' => 'Valor enlace');

$addSubField[] = array( 
'nombre_enlace' => '2 multidimensional',
'enlace' => 'Valor enlace 2');

And now the call:

insert_field_subfield('spoiler', 'enlaces', $addField, $addSubField, 'field_586155ce49bf8', 21267);
insert_field_subfield('spoiler', 'enlaces', $addField, $addSubField, 'field_586155ce49bf8', 21267);

I hope this will be useful for somebody!

P.S. I it's not perfect because I don't control the second repeater, but this is something that don't have ACF API.

P.S. field_key is the field key of the first repeater.

Upvotes: 1

RCNeil
RCNeil

Reputation: 8759

I was able to accomplish this with both a combination of ACF's functions and regular good old PHP. I can't find another method, but I read other posts where people allude to this method, although they do not elaborate or show code, so here were my long-winded steps to accomplish this:

  1. Obtain the entire array of your parent repeater
  2. Loop through until you hit the row with the nested repeater you want to edit
  3. Figure out if it is an array or not (if it isn't, make it an array)
  4. Append the child repeater row using array_push()
  5. Save the entire parent array again using update_field()

To help illustrate the parent/child relation, remember I have a repeater field called media and a nested repeater within that called notes, which only has the 1 sub-field note. So if I use get_field('media') this is the array it would return:

Array
(
    [0] => Array
        (
          [media_title] => 'title 1',
          [notes] => Array
            (
                [0] => Array
                    (
                        [note] => 'HERE IS THE NOTE ADDED'
                    )

                [1] => Array
                    (
                        [note] => 'Here is another note added!'
                    )
            )
    )
    [1] => Array
        (
          [media_title] => 'title 2',
          [notes] => 
    )
 )

So, in this example, I have 2 elements in the parent repeater, one of which has 2 elements in the nested repeater, one that has no elements in the nested repeater. This is important because you will need to determine if there is already an array established in the nested repeater before you can add to it.

//LOOP THROUGH PARENT REPEATER
if( have_rows('media_item', $postID) ): 

    //SET YOUR ROW COUNTER
    $i = 0;

    //MAKE YOUR NEW ARRAY
    $media_item = get_field('media_item', $postID); 

    while( have_rows('media_item', $postID) ) : the_row();          
        if($i == $arrayRowID) { //IF THIS IS THE ROW WE WANT TO EDIT IN PARENT REPEATER                 
            //FIND OUT IF IT IS AN ARRAY ALREADY OR NOT
            if( !is_array($media_item[$i]['notes']) ) {  //WE DONT HAVE ANY NOTES
                $media_item[$i]['notes'] = array();             
            }               
            $addRow = array( //SET YOUR NEW ROW HERE
                'note' => 'This is my new note added!';
            );                  
            //ADD TO ARRAY
            array_push($media_item[$i]['notes'], $addRow);              
        }           
    $i++;
    endwhile;
endif;

So, I use $i as a counter to determine which row in the parent repeater I want. If it was the second item in the array, $i = 1, since the array starts at 0. You also have to feed it your post ID in this function, but hopefully it makes sense. Be careful if you have many rows for your parent or child repeaters, as to save it, you have to overwrite the entire parent repeater array with the new one you just created:

$field_key = "field_xxxxxxxxxxxxx" //FIELD KEY OF PARENT REPEATER 'media'
update_field($field_key, $media_item, $postID); 

This will save the new array with the rows you appended to your child repeater. I hope this helps someone as I couldn't find any examples covering this method. I would love to a function added to ACF like add_row() which works for nested repeaters!

Upvotes: 3

Related Questions