Support

Account

Home Forums Add-ons Repeater Field Using acf_form() to store/fetch a multidimensional array meta_key

Solving

Using acf_form() to store/fetch a multidimensional array meta_key

  • Hi there,

    We’re using the acf_form() function to update a custom post type on the front-end. So far, the plugin has been working absolutely brilliantly; we are just getting stuck on one issue: we would like to update/edit values that are stored as a multidimensional array in a pre-existing _postmeta meta_key field via an ACF repeater field.

    I realise that this post might be a bit esoteric and a tad bit complicated, so I’ve done my best to outline things in the most detailed way that I can.

    I’ve thrown a ton of hours at this and would appreciate any insights, so thanks in advance to anyone who might be able to help.

    Here’s what we have:

    1) The value stored in my field _my_array_field (this is the default format when the Custom Post Type is edited within WP):

    
    a:1:{i:0;a:5:{s:4:"type";s:5:"type2";s:8:"yesno";s:3:"yes";s:8:"priority";i:10;s:4:"nummin";s:1:"2";s:2:"nummax";s:1:"3";}}
    

    2) I’ve figured out that the array (echoed as a variable) that would cause this output is:

    
    <?php
    	$array = array(
    		array(
    			array(
    				"type" => "type1",
    				"yesno" => "yes",
    				"priority" => "10",
    				"nummin" => "1",
    				"nummax" => "2",
    			),
    			array(
    				"type" => "type2",
    				"yesno" => "no",
    				"priority" => "2",
    				"nummin" => "333",
    				"nummax" => "444",
    			),
    			array(
    				"type" => "type3",
    				"yesno" => "no",
    				"priority" => "1",
    				"nummin" => "444",
    				"nummax" => "555",
    			),
    		),
    	);
    ?>
    

    3) In ACF, we have a repeater field for ‘_my_array_field’ and it’s ID is ‘_my_array_field.’ For the child fields, we have the nested array labels accordingly: ‘type,’ ‘yesno,’ ‘priority,’ ‘nummin,’ ‘nummax’.

    When we display the value of the acf field via the get_field() function, we are able to replicate the multi-dimensional array exactly.

    4) We have a page template rendering our ACF form using

    
    <?php
    	$options = array(
    		'post_id'	=> $post_id,
    		'post_title'	=> true,
    		'field_groups' => array(
    			123
    		),
    		'updated_message' => __("Post updated", 'acf'),
    		'submit_value'	=> 'Update'
    	);
    ?>
    

    5) When we update the Custom Post Type from our front-end ACF form, this happens:

    a) The ‘_my_array_field’ is empty in the _postmeta table for the CPT in questions

    b) Rows have been created for each ‘child’ field in the repeater such as:

    	
    	meta_key			| meta_value
    	_my_array_field_1_type 		| type1
    	__my_array_field_1_type  	| field_randostuff1
    	_my_array_field_1_yesno		| yes
    	__my_array_field_1_yesno 	| field_randostuff2
    	_my_array_field_1_priority	| 1
    	__my_array_field_1_priority	| field_randostuff3
    	_my_array_field_1_nummin	| 123
    	__my_array_field_1_nummin	| field_randostuff4
    	_my_array_field_1_nummax	| 456
    	__my_array_field_1_nummax	| field_randostuff5
    	_my_array_field_2_type 		| type1
    	__my_array_field_2_type  	| field_randostuff6
    	_my_array_field_2_yesno		| yes
    	__my_array_field_2_yesno 	| field_randostuff7
    	_my_array_field_2_priority	| 1
    	__my_array_field_2_priority	| field_randostuff8
    	_my_array_field_2_nummin	| 123
    	__my_array_field_2_nummin	| field_randostuff9
    	_my_array_field_2_nummax	| 456
    	__my_array_field_2_nummax	| field_randostuff10
    

    …etc, etc

    6) When we edit the multi-dimensional array field ‘_my_array_field’ from within WordPress, the ACF repeater field renders ‘row,’ but the values don’t seem to be passing through

    So, now my question(s), how can we store/fetch the repeater values using the acf_form() function so that

    1) when posting on the front-end, the ACF repeater field value gets stored as a multidimensional array into a pre-existing meta_key > meta_value?

    2) how an ACF repeater field can render/parse values from a pre-existing multidimensional array meta_key > meta_value on the front-end?

    Is there some kind of a hook / function that will allow us to store the values created through something like ‘foreach’ for our repeater field? How might this be accomplished?

    Many thanks in advance!

    Ryan

  • Repeaters do not store values as a multidimensional array. If you must have the field that holds a multidimensional array and this field already exists then you’re going to need to have two ways of storing the data.

    The first thing that you’re going to need to do is create an acf/load_value filter http://www.advancedcustomfields.com/resources/acfload_value/ for the repeater. The array is already formatting the way it needs to be, you just need to use the value from the other field.

    
      
      add_filter('acf/load_value/name=my_repeater_field',
                        'load_my_repeater_field_value', 10, 3);
      function  load_my_repeater_field_value($value, $post_id, $field) {
        $load_value =  maybe_unserialize(get_post_meta($post_id, 'the_original_field', true));
        return $load_value;
      }
    

    The second thing you’re going to need to do is create an acf/save_post filter http://www.advancedcustomfields.com/resources/acf_save_post/ for the repeater that puts the new value back into your original field. This filter needs to run after ACF has updated the field value.

    
      
      add_filter('acf/save_post', 'update_my_repeater_field_value', 20);
      function update_my_repeater_field_value($post_id) {
        // check the post type
        // you don't want to do this on every post type
        if (get_post_type($post_id) != 'my_post_type') {
          return;
        }
        $value = get_field('my_repeater_field', $post_id);
        update_post_meta($post_id, 'the_original_field', $value);
      }
    
  • Hi John,

    Thanks for the response!

    I’ve been fiddling with the code that you sent. The good news is with your code, I’m able to get empty rows to show in the repeater field. The not-so-good news is I’m still not able to pre-populate the form rows with values on the front end, nor post the values to the correct pre-existing field.

    I’ve noticed that the pre-existing field (‘availability’ field, we’re using WooCommerce Bookings) is wrapping the item in an extra array, such as:

    
    Array ( [0] => Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 2016-02-02 [to] => 2016-02-03 ) [1] => Array ( [type] => custom [bookable] => no [priority] => 10 [from] => 2016-02-10 [to] => 2016-02-12 ) [2] => Array ( [type] => custom [bookable] => no [priority] => 10 [from] => 2016-02-04 [to] => 2016-02-20 ) ) ) 
    

    …whereas, the array output from ACF using your code is:

    
    Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 2016-02-02 [to] => 2016-02-03 ) [1] => Array ( [type] => custom [bookable] => no [priority] => 10 [from] => 2016-02-10 [to] => 2016-02-12 ) [2] => Array ( [type] => custom [bookable] => no [priority] => 10 [from] => 2016-02-04 [to] => 2016-02-20 ) ) 
    

    The code I’m using so far:

    
    <?php
    /**
     * The template for displaying full width pages.
     *
     * Template Name: ACF Front End Repeater Form
     *
     * @package storefront
     */
    ?>
    <?php $post_id = 55; ?>
    <?php acf_form_head(); ?>
    <?php get_header(); ?>
    
    	<div id="primary" class="content-area">
    		<main id="main" class="site-main" role="main">
    
    		<?php
    			
    		add_filter('acf/load_value/name=my_repeater_field','load_my_repeater_field_value', 10, 3);
    		
    		function  load_my_repeater_field_value($value, $post_id, $field) {
    			/* 
    				--> adding the array() around get_post_meta() makes this output array match the default one (see line 57 below please), but it does not render form rows
    				
    				$load_value =  maybe_unserialize(get_post_meta($post_id, '_wc_booking_availability', true)); 
    			*/
    			$load_value =  maybe_unserialize(get_post_meta($post_id, '_wc_booking_availability', true));
    			return $load_value;
    			/* 
    				--> tried this, but it added extra empty rows somehow
    
    				return call_user_func_array('array_merge', $load_value);
    			*/
    		};
    		
    		?>
    			
    			<?php
    				$options = array(
    					'post_id' => $post_id,
    					'post_title' => true,
    					'field_groups' => array(
    						18,
    					),
    					'submit_value' => __("Update", 'acf'),
    					'updated_message' => __("Post updated", 'acf'),
    					'uploader' => 'wp'
    				);
    			?>
    			<?php acf_form($options); ?>
    			<?php 
    			add_filter('acf/save_post', 'update_my_repeater_field_value', 20);
    			function update_my_repeater_field_value($post_id) {
    				// check the post type
    				// you don't want to do this on every post type
    				if (get_post_type($post_id) != 'product') {
    					return;
    				}
    				$value = get_field('my_repeater_field', $post_id);
    				update_post_meta($post_id, '_wc_booking_availability', $value);
    			} // when i submit the field, the ACF array (output below) deletes all the values
    			?>
    			
    			<?php
    				// Printing both the pre-existing field and the ACF field arrays for debugging/testing
    				
    				// ACF
    				$acf_repeater_array = get_field( "my_repeater_field", $post_id);
    				echo 'ACF repeater field array:<br><br>';
    				if( empty( $acf_repeater_array ) ){
    					echo '<strong>Empty</strong><br><br>';
    				} else {
    					print_r(array_values($acf_repeater_array)); // this doesn't match the array for the pre-existing field unless i wrap get_post_meta() in an array() on line 25
    					echo '<br><br><hr>';
    				};
    			
    				// Pre-existing field
    				$pre_existing_array = get_post_meta( $post_id, '_wc_booking_availability' );
    				echo  'Pre-existing field array:<br><br>';
    				if( empty( $pre_existing_array ) ){
    					echo '<strong>Empty</strong><br><br>';
    				} else {
    					print_r(array_values($pre_existing_array));
    					echo '<br><br><hr>';
    					echo 'Pre-existing field array (when I \'flatten\' the pre-existing array):<br><br>';
    					$flat = call_user_func_array('array_merge', $pre_existing_array);
    					print_r(array_values($flat)); // this DOES match the array for the pre-existing field 
    					echo '<br><br><hr>';
    				};
    			?>
    
    		</main><!-- #main -->
    	</div><!-- #primary -->
    
    <?php get_footer(); ?>
    
    

    I’m at the point where I don’t know if I’m making progress or just getting more lost, so huge, huge thanks in advance for any insights!

  • The first thing you’re going to want to do is take the filters out of your template and put them in your functions.php file.

    With the additional information about the fields and arrays I think the following should make it work.

    
    <?php
          
      add_filter('acf/load_value/name=my_repeater_field','load_my_repeater_field_value', 10, 3);
      
      function  load_my_repeater_field_value($value, $post_id, $field) {
        $load_value =  maybe_unserialize(get_post_meta($post_id, '_wc_booking_availability', true));
        if (is_array($load_value)) {
          // this will remove the extra array wrapper
          $load_value = $load_value[0];
        }
        if (is_array($load_value)) {
          // this is seperate
          // just in case the above does not return a nested array
          // you need to format some of the values differently for acf
          // acf stores dates without the -
          foreach ($load_value as $index => $row) {
            $load_value[$index]['from'] = str_replace('-', '', $load_value[$index]['from']);
            $load_value[$index]['to'] = str_replace('-', '', $load_value[$index]['to']);
          }
        }
        return $load_value;
      };
      
      
      add_filter('acf/save_post', 'update_my_repeater_field_value', 20);
      function update_my_repeater_field_value($post_id) {
        // check the post type
        // you don't want to do this on every post type
        if (get_post_type($post_id) != 'product') {
          return;
        }
        $value = array(get_field('my_repeater_field', $post_id));
        update_post_meta($post_id, '_wc_booking_availability', $value);
      }
    
    ?>
    
  • Hi John,

    Thanks for the super-quick response. I’ve removed those filters from the page template and placed the code that you’ve provided in my functions.php.

    This however, triggered some errors:

    
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Cannot use a scalar value as an array in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Cannot use a scalar value as an array in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'from' in ~/wp-content/themes/storefront/functions.php on line 32
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    Warning: Illegal string offset 'to' in ~/wp-content/themes/storefront/functions.php on line 33
    
    

    I’m going to start investigating these myself, but thought I’d send you this along already if you know why this is happening.

    Thanks in advance!

  • Hi again,

    I created a ‘clean’ post to re-test this and am posting what i found in hopes that it helps a bit. I found that the array being output from ACF is:

    
    
    Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 20160223 [to] => 20160216 ) ) 
    
    

    the value from the pre-existing field straight from WordPress is

    
    
    Array ( [0] => Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 2016-02-23 [to] => 2016-02-16 ) ) ) 
    
    

    Thanks again!

  • The only thing that I can think of is that the original field may not have a value at some point that that’s messing it up. I added some more error checking to the first array.

    If this this doesn’t help then you’ll need to start dumping some values to the screen at each point to see what’s going on.

    
    <?php
          
      add_filter('acf/load_value/name=my_repeater_field','load_my_repeater_field_value', 10, 3);
      
      function  load_my_repeater_field_value($value, $post_id, $field) {
        $load_value =  maybe_unserialize(get_post_meta($post_id, '_wc_booking_availability', true));
        if (!is_array($load_value)) {
          return $value;
        }
        if (is_array($load_value)) {
          // this is seperate
          // just in case the above does not return a nested array
          // you need to format some of the values differently for acf
          // acf stores dates without the -
          foreach ($load_value as $index => $row) {
            if (count($row)) {
              $load_value[$index]['from'] = str_replace('-', '', $load_value[$index]['from']);
              $load_value[$index]['to'] = str_replace('-', '', $load_value[$index]['to']);
            } // end if count row
          } // end foreach row
        } // end if is array
        return $load_value;
      };
      
      
      add_filter('acf/save_post', 'update_my_repeater_field_value', 20);
      function update_my_repeater_field_value($post_id) {
        // check the post type
        // you don't want to do this on every post type
        if (get_post_type($post_id) != 'product') {
          return;
        }
        $value = array(get_field('my_repeater_field', $post_id));
        update_post_meta($post_id, '_wc_booking_availability', $value);
      }
    
    ?>
    
  • Hi John,

    Thanks again for another response!

    I’ve implemented the code that you sent and see that:

    1) the ACF form is still loading the row, but the values are empty
    2) when i click the ‘update button on the ACF form, I get the following error

    
    Warning: Cannot modify header information - headers already sent by (output started at ~/wp-content/themes/storefront/template-acf.php:12) in ~/wp-includes/pluggable.php on line 1228
    

    In my page template, I’ve the following code to see what’s going on with the arrays:

    
    
    			<?php
    				// Printing both the pre-existing field and the ACF field arrays for debugging/testing
    				
    				// ACF
    				echo 'ACF repeater field array from load value filter:<br><br>';
    				if( empty( $load_value ) ){
    					echo '<strong>Empty</strong><br><br>';
    				} else {
    					print_r(array_values($load_value)); // this doesn't match the array for the pre-existing field unless i wrap get_post_meta() in an array() on line 25
    					echo '<br><br><hr>';
    				};
    
    				// ACF
    				$acf_repeater_array = get_field( "my_repeater_field", $post_id);
    				echo 'ACF repeater field array:<br><br>';
    				if( empty( $acf_repeater_array ) ){
    					echo '<strong>Empty</strong><br><br>';
    				} else {
    					print_r(array_values($acf_repeater_array)); // this doesn't match the array for the pre-existing field unless i wrap get_post_meta() in an array() on line 25
    					echo '<br><br><hr>';
    				};
    			
    				// Pre-existing field
    				$pre_existing_array = get_post_meta( $post_id, '_wc_booking_availability' );
    				echo  'Pre-existing field array:<br><br>';
    				if( empty( $pre_existing_array ) ){
    					echo '<strong>Empty</strong><br><br>';
    				} else {
    					print_r(array_values($pre_existing_array));
    				};
    			?>
    
    

    ACF repeater field array from load value filter:

    
    Empty
    

    ACF repeater field array:

    
    Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 20160211 [to] => 20160219 ) )
    

    Pre-existing field array:

    
    Array ( [0] => Array ( [0] => Array ( [type] => custom [bookable] => yes [priority] => 10 [from] => 2016-02-11 [to] => 2016-02-19 ) ) ) 
    

    In case this helps, the value pasted directly from phpMyadmin for the pre-existing array field (_wc_booking_availability) is:

    
    
    a:1:{i:0;a:5:{s:4:"type";s:6:"custom";s:8:"bookable";s:3:"yes";s:8:"priority";i:10;s:4:"from";s:10:"2016-02-11";s:2:"to";s:10:"2016-02-19";}}
    
    

    I really appreciate your effort and time with helping me on this, so thanks again in advance!

  • looking it over I missed something in the last code I posted

    
    add_filter('acf/load_value/name=test_repeater','load_my_repeater_field_value', 10, 3);
      
      function  load_my_repeater_field_value($value, $post_id, $field) {
        $load_value =  maybe_unserialize(get_post_meta($post_id, 'inserted_into_repeater', true));
        if (!is_array($load_value)) {
          return $value;
        }
        if (is_array($load_value)) {
          $load_value = $load_value[0];
          foreach ($load_value as $index => $row) {
            if (count($row)) {
              $load_value[$index]['from'] = str_replace('-', '', $load_value[$index]['from']);
              $load_value[$index]['to'] = str_replace('-', '', $load_value[$index]['to']);
            } // end if count row
          } // end foreach row
        } // end if is array
        return $load_value;
      };
      
      
      add_filter('acf/save_post', 'update_my_repeater_field_value', 20);
      function update_my_repeater_field_value($post_id) {
        // check the post type
        // you don't want to do this on every post type
        if (get_post_type($post_id) != 'product') {
          return;
        }
        $value = get_field('my_repeater_field', $post_id);
        if (is_array($value)) {
          foreach ($value as $index => $row) {
            if (count($row)) {
              $value[$index]['from'] = date('Y-m-d', strtotime($value[$index]['from']));
              $value[$index]['to'] = date('Y-m-d', strtotime($value[$index]['to']));
            } // end if count row
          } // end foreach row
        }
        $value = array($value);
        update_post_meta($post_id, '_wc_booking_availability', $value);
      }
    
  • Hey John,

    Thanks again for another response!

    I’ve implemented the code and have found the following:

    1) when I create a new, clean product, with no value in the field where the multidimensional array is stored, I see the five rows in my form and the following error output:

    
    Notice: Undefined offset: 0 in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 26
    
    Warning: Invalid argument supplied for foreach() in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 27
    

    2) when i do create a field (via WooCommerce’s Booking plugin…the ‘_wc_booking_availability’ field, on the front-end ACF form, I see

    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Cannot use a scalar value as an array in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Cannot use a scalar value as an array in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'from' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 29
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    
    Warning: Illegal string offset 'to' in /Applications/MAMP/htdocs/acf/wp-content/themes/storefront/functions.php on line 30
    

    I really do appreciate your time in helping me with this so thanks again in advance. I don’t want to violate any kind of policy of ACF’s forum by soliciting services or whatnot, but would it be possible for me just to hire you to knock this out? I have the ability to create an environment and furnish access quickly.

    Thanks again!

  • This reply has been marked as private.
Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.