Support

Account

Home Forums Add-ons Flexible Content Field Issues with add_rows and flexible content

Solved

Issues with add_rows and flexible content

  • I also created a support ticket, but wanted to open this up to the forum as well for findability and posterity.

    I’m using information found here: https://www.advancedcustomfields.com/resources/add_row/ and here: https://support.advancedcustomfields.com/forums/topic/using-add_row-with-flexible-content/

    I’ve got a plugin that’s creating pages via some ajax and wp api calls. I am wanting to update it so that it can populate a flexible content field ‘modules’ on each page. I have a hook on save_post’ that reads a json string from the post content into a json object of the layouts I want to add to the page. Then, I’m looping through any layouts and using add_row to send my data like so:

    $row_i = add_row('field_55ce0e107c687', array( 'acf_fc_layout'=>'headline', 'field_55df18296e4b3'=>'headline text'), $ID);

    After some research I thought I had it all figured out, but it doesn’t seem that the add_row function is working properly. First, I can add one layout and it works fine, but if I loop through an array of layouts and add them each, when I actually go to the edit screen for that page, the fields don’t load. Looking at the database it seems it’s not saving the flexible content rows properly.

    Then, I notice that after I’ve added a value in this way (just one), I can go to the page in the edit screen and if I add more then after I save the page, when the page loads again, the flexible content rows are all gone. This even occurs when I don’t make any changes and just hit the update button. So perhaps the first one that gets through doesn’t completely set itself up properly.

    I’ve done some comparisons on what it looks like in the database and it looks like something with the serialized data in the main flexible content field. The initial insert seems to work fine, but then any additional (and I’m not sure why it’d happen on save) flexible content rows don’t display as serialized data anymore, it’s just an integer (and it seems to be the count of the rows).

    I wanted to know if there is something I’m missing or if this may be some sort of bug. I could totally be misunderstanding how to use this add_row function. Looking through the add_row source and it looks like it’s checking to see if it’s the first row and handles that ok, but if it’s not the first row, it’s just adding the value as key value pairs. But line 1763 seems to be overwriting the serialized data with the row count. Since each time I’m sending in the same key selector ‘field_55ce0e107c687’ is that confusing the system? I’m not sure what else to use though. I’m seeing fields when I look in the database with names like ‘modules_0_acf_fc_layout’ which seems to be like the format isn’t correct.

    Here’s my whole ‘save_post’ hook:

    function publish_new_page( $ID, $post ) {
    	// set content and modules as set in sitemap/json
    	if ( $post->post_type === 'page' &&
    		 $post->post_status !== 'auto-draft' &&
    		get_post_meta('publish_new_page') !== 'true' ) {
    
    		//set flag that this post has been updated
    		update_post_meta($ID, 'publish_new_page', 'true');
    
    		//read module json data from the content
    		$content = json_decode( get_post_field('post_content', $ID) );
    		
    		//flexible content field key
    		$fc_key = 'field_55ce0e107c687';
    		$fc_rows = [];
    
    		//read json stored in content for this post and use it to add rows to the flexible content
    		if ( is_array($content) ) {
    			foreach($content as $row){
    				switch($row->layout){
    					case 'billboard':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'billboard', 'field_55ce0ed27c68a'=>$row->headline), $ID);
    						break;
    					case 'callout':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'callout', 'field_564e22d0da4ef'=>$row->headline), $ID);
    						break;
    					case 'content':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'content', 'field_55ce0e6c7c688'=>$row->headline), $ID);
    						break;
    					case 'headline':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'headline', 'field_55df18296e4b3'=>$row->headline), $ID);
    						break;
    					case 'recent':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'recent', 'field_55e5d3f2b7ec7'=>$row->headline), $ID);
    						break;
    					case 'subheadline':
    						$row_i = add_row($fc_key, array( 'acf_fc_layout'=>'subheadline', 'field_55df18be00e56'=>$row->headline), $ID);
    						break;
    					default:
    						//unknown layout type - skip
    						$row_i = update_post_meta($ID, 'module_error', json_decode($row) );
    				}
    				array_push($fc_rows, $row_i);
    			}
    		}
    		//some debugging custom fields
    		update_post_meta($ID, 'modules_set', json_encode($content));
    		update_post_meta($ID, 'fc_rows', json_encode($fc_rows));
    	}
    
    }
    add_action( 'save_post', 'publish_new_page', 10, 3 );
  • Looking over the ACF add_row() code myself I can tell you that add_row() as it works now can definitely not be used to add a layout to a flexible content field.

    The value of a repeater fields meta value is the number of rows in the repeater. The value of a flex fields meta value is a serialized array of layout names. Since add_row() always updates the meta value to a count of rows it cannot function for adding a layout.

    It may work if there are 0 layouts, I did not investigate what happens with 0 rows when this line is called.

    
    return (int) acf_update_value( array( $value ), $post_id, $field );
    

    but, if there is a value then this line converts the array of field names to 1

    
    $i = (int) acf_get_metadata( $post_id, $field['name'] );
    

    this can be tested by

    
    echo (int) array('test');
    

    That means that if( !$i ) { is not executed

    and then $i is incremented and saved to the meta value

    
    // increase $i
    $i++;
    // update meta
    $result = acf_update_metadata( $post_id, $field['name'], $i );
    

    which overwrites the array for the flex field with an integer, destroying the needed array.

  • Right, my thoughts exactly. Add row does create the FC properly if there isn’t anything saved in it yet, it lets you add one row, but trying a second the same way busts it.

    I will try out update_field() now and see what it can do. Thanks!

  • I’m not 100% sure what you should send to update field for a flex field.

    I know that for a repeater you can so something like this:

    
    $repeater = array(
      // nested array for each row
      array(
        // element for each field
        'field_1234567890' => 'value',
      )
    );
    

    I would definitely use field keys here, especially if you’ll be adding values that don’t exist yet, like the first row.

    Most of the time, if you do the following you can see what you need to supply for update_field(). What is returned here usually works in reverse for updating if you replace field names with field keys. But again, I’m not 100% sure on the flex field.

    
    $flex_field = get_field('my_flex_field', false, false);
    echo '<pre>'; print_r($flex_field); echo '</pre>';
    
  • Sweet, I’ve got it figured out! update_field was the trick.

    Before I was looping through a bunch of rows and attempting to add them each individually with add_row, but now I’m looping through and creating an array of fields and then add them all at once with update_field. This works because I’m only doing this on new pages and therefore the flexible content field is always empty. I imagine that if there was any content in the FC at the time this was run then the existing content would be overridden. Here’s the updated code that’s working for reference and posterity:

    function bagpress_sitemapper_new_page( $ID, $post, $update ) {
    	// set content and modules as set in sitemap/json
    	if ( $post->post_type !== 'page' ||
    		 $post->post_status === 'auto-draft' ||
    		get_post_meta('modules') ) {
    		//if not a page or an autodarft or has already been updated or has any content in the modules already, don't update it. get out!
    		//use modules custom field as the flag since it will exist automatically in there are any modules. and in that case we don't want to add any more
    		return;
    	}
    	
    	//read module json data from the content
    	$content = json_decode( get_post_field('post_content', $ID) );
    	//if not array/json end this
    	if ( !is_array($content) ) {
    		return;
    	}
    
    	//flexible content field key
    	$fc_key = 'field_55ce0e107c687';
    	$fc_rows = [];
    
    	//read json stored in content for this post and use it to add rows to the flexible content
    	foreach($content as $row){
    		switch($row->layout){
    			case 'billboard':
    				$row_i = array( 'acf_fc_layout'=>'billboard', 'field_55ce0ed27c68a'=>$row->headline);
    				break;
    			case 'callout':
    				$row_i = array( 'acf_fc_layout'=>'callout', 'field_564e22d0da4ef'=>$row->headline);
    				break;
    			case 'content':
    				$row_i = array( 'acf_fc_layout'=>'content', 'field_55ce0e6c7c688'=>$row->headline);
    				break;
    			case 'headline':
    				$row_i = array( 'acf_fc_layout'=>'headline', 'field_55df18296e4b3'=>$row->headline);
    				break;
    			case 'recent':
    				$row_i = array( 'acf_fc_layout'=>'recent', 'field_55e5d3f2b7ec7'=>$row->headline);
    				break;
    			case 'subheadline':
    				$row_i = array( 'acf_fc_layout'=>'subheadline', 'field_55df18be00e56'=>$row->headline);
    				break;
    			default:
    				//unknown layout type - skip
    				// $row_i = update_post_meta($ID, 'module_error', json_decode($row) );
    		}
    		array_push($fc_rows, $row_i);
    	}
    	update_field($fc_key, $fc_rows, $ID);
    	
    	//turn off save_post hook so we can update the post without triggering it
    	remove_action('save_post', 'bagpress_sitemapper_new_page', 10, 3);
    	//remove json data from content
    	wp_update_post( array(
    		'ID' => $ID,
    		'post_content' => ''	
    	));
    	//turn save_post hook back on
    	add_action( 'save_post', 'bagpress_sitemapper_new_page', 10, 3);
    
    }
    add_action( 'save_post', 'bagpress_sitemapper_new_page', 10, 3 );

    If one wanted to use update_field to update a field that already contained existing content, I don’t think it’d be too hard to first read that field and put the contents into an array and then add the new rows/layouts to the array and call update_field sending in the new array.

  • Hopefully, other people that are trying to use add_row() on a flex field will find this. The documentation that says it can be used for flex fields is obviously wrong now, even it it was correct at some time in the past.

  • Yea, that line in the docs is why I was trying to do it with add_row in the first place. Although it wasn’t until I read the update_field docs that I knew to use the "acf_fc_layout" keys and finally got it working for one row. The more I looked at add_row source the more I thought it wouldn’t do what it said it did, but for some reason I didn’t ever try update_field. Thanks for waking me up to it, it works like a charm. I’ll file a ticket to update the docs.

  • Hi ,
    Is there a way to insert multiple values to a same field key.
    So that in the db it will insert like field_1_val,field_2_val like wise for a same field key..

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

You must be logged in to reply to this topic.