Support

Account

Home Forums Backend Issues (wp-admin) Triggering the Advanced Custom Fields (ACF) \'acf/save_post\' Action

Solving

Triggering the Advanced Custom Fields (ACF) \'acf/save_post\' Action

  • I’ve read the ACF ‘acf/save_post’ documentation here: https://www.advancedcustomfields.com/resources/acf-save_post/, which states this action enables one to add additional functionally before or after saving/updating a post. I want to test when this action is triggered.

    I created a custom post type and a collection of ACF custom fields that are linked to this custom post type. This includes some ACF custom field types that I created using the ACF Starter Kit, and they are used for some of the ACF custom fields. When I edit a custom post and then click the ‘Update’ button in the Gutenberg editor, I expect the ‘acf/save_post’ action to fire, but it does not seem to do so. Perhaps I am missing something. Here is the relevant code from my theme’s ‘function.php’ file.

    function my_acf_save_post($post_id) {
      console_log("Testing...");
    }
    add_action('acf/save_post', 'my_acf_save_post', 20);
    
    function console_log($output, $with_script_tags = true) {
      $js_code = 'console.log(' . json_encode($output, JSON_HEX_TAG) . ')';
    
      if($with_script_tags) {
        $js_code = '<script>' . $js_code . '</script>';
      }
    
      echo $js_code;
    }

    Citation for the ‘console_log’ function: Kim Sia (see: https://stackify.com/how-to-log-to-console-in-php/).

    I have my web browser’s inspector window opened for my custom post type post’s editor. After modifying the post and clicking ‘Update’, nothing displays in the inspector’s console pane. I posted about this in the ACF forum but have received no response.

    Your feedback is appreciated. Thank you.

  • The code you are trying to run happens when the post is saved, not when the edit page is being displayed. Sequence of actions
    1) Submit post
    2) Post data is saved
    3) Redirect to and reload post edit page
    You are attempting to output HTML during an operation that does not product output. I’m surprised that you are not getting PHP errors about headers already sent.

  • I appreciate the reply, John. Here is the full ‘acf/save_post’ description from :

    Fires when saving the submitted $_POST data.

    This action allows you to hook in before or after the $_POST data has been saved, making it useful to perform additional functionality when updating (emphasis mine) a post or other WP object.

    Digging deeper, I ran a search for the the “acf/save_post” WordPress “add_action” action hook, which took me to the file “..\plugins\advanced-custom-fields\includes\acf-form-functions.php”. Here is a code excerpt from this file:

    /**
     * _acf_do_save_post
     *
     * Private function hooked into 'acf/save_post' to actually save the $_POST data.
     * This allows developers to hook in before and after ACF has actually saved the data.
     *
     * @date	11/1/19
     * @since	5.7.10
     *
     * @param	int|string $post_id The post id.
     * @return	void
     */
    function _acf_do_save_post( $post_id = 0 ) {
    	
    	// Check and update $_POST data.
    	if( $_POST['acf'] ) {
    		acf_update_values( $_POST['acf'], $post_id );
    	}	
    }
    
    // Run during generic action.
    add_action( 'acf/save_post', '_acf_do_save_post' );

    The “acf_update_values” function is located in the file “..\plugins\advanced-custom-fields\includes\acf-value-functions.php” in the following code excerpt:

    /**
     * acf_update_values
     *
     * Updates an array of values.
     *
     * @date	26/2/19
     * @since	5.7.13
     *
     * @param	array values The array of values.
     * @param	(int|string) $post_id The post id.
     * @return	void
     */
    function acf_update_values( $values, $post_id ) {
    	
    	// Loop over values.
    	foreach( $values as $key => $value ) {
    		
    		// Get field.
    		$field = acf_get_field( $key );
    		
    		// Update value.
    		if( $field ) {
    			acf_update_value( $value, $post_id, $field );
    		}
    	}
    }

    The “foreach” loop invokes the “acf_update_value” function that is located in the same PHP file. Here is its code excerpt:

    /**
     * acf_update_value
     *
     * Updates the value for a given field and post_id.
     *
     * @date	28/09/13
     * @since	5.0.0
     *
     * @param	mixed $value The new value.
     * @param	(int|string) $post_id The post id.
     * @param	array $field The field array.
     * @return	bool.
     */
    function acf_update_value( $value, $post_id, $field ) {
    	
    	// Allow filter to short-circuit update_value logic.
    	$check = apply_filters( "acf/pre_update_value", null, $value, $post_id, $field );
    	if( $check !== null ) {
    		 return $check;
    	}
        
        /**
    	 * Filters the $value before it is updated.
    	 *
    	 * @date	28/09/13
    	 * @since	5.0.0
    	 *
    	 * @param	mixed $value The value to update.
    	 * @param	string $post_id The post ID for this value.
    	 * @param	array $field The field array.
    	 * @param	mixed $original The original value before modification.
    	 */
    	$value = apply_filters( "acf/update_value", $value, $post_id, $field, $value );
    	
    	// Allow null to delete value.
    	if( $value === null ) {
    		return acf_delete_value( $post_id, $field );
    	}
    	
    	// Update meta.
    	$return = acf_update_metadata( $post_id, $field['name'], $value );
    	
    	// Update reference.
    	acf_update_metadata( $post_id, $field['name'], $field['key'], true );
    	
    	// Delete stored data.
    	acf_flush_value_cache( $post_id, $field['name'] );
    	
    	// Return update status.
    	return $return;
    }

    The relevant line of code is the “acf_update_metadata” statement located just below the “// Update reference.” comment. In the interest of reducing this post’s size:

    The “acf_update_metadata” function is in the “..\acf-meta-functions.php” file. One of last code lines invokes the “update_metadata” function that is located in the “..\meta.php” file.

    The “update_metadata” function includes the following “do_action”, which according to the code comments “fires immediately before updating a post’s metadata”:

    do_action( "update_postmeta', $meta_id, $object_id, $meta_key, $meta_value' );

    According to this sequence of function invocations, when I edit an ACF custom field in a post’s editor and then click the ‘Update’ button to update the post’s value in the WordPress database’s post table, this triggers a sequence of events that includes updating the WordPress post metadata table.

    In actually, neither the post metadata table is update nor does

    add_action('save_post', 'my_save_post');

    appear to be invoked from

    do_action( "update_postmeta', $meta_id, $object_id, $meta_key, $meta_value' );

    Perhaps, I’ve missed something. Again, your feedback is appreciated.

  • The action function that you posted is not making any changes to fields. It is attempting to create output that is sent to the browser. Output is not created and sent to the browser during the update process. Trying to do so is likely creating causing errors.

  • John, thanks again for your feedback. Perhaps you can point me in the right direction. Based upon your responses, the “acf/save_post” action does not respond to WordPress post creations/updates. Frankly, the ACF documentation is a bit confusing. I suppose its purpose is to enable one to update the relevant WordPress metadata table, among other WordPress metadata-specific tasks, whether updating the “standard” WP metadata tables or user-defined WP metadata tables.

    Is this an accurate statement?

    The detailed post I provided in this thread traces through a series of ACF-specific function calls that lead to WP metadata table row additions/modifications/deletions. For example, one such trace extends to the “meta.php” file contained in the WordPress “wp-includes” directory.

    So, if the “acf/save_post” action is solely used to update a post’s metadata, for instance, metadata created via ACF, and if I want both to update a post’s metadata via a post editor window AND update the post itself, how best would I do so?

    Thank you.

  • Yes, acf/save_post is to allow you to do other things related to updating a field or a post. This can be anything that does not create “output” like changing meta data, sending information to a 3rd party API, altering the post in some way.

    If you want to update the meta data or the post then yes, you would use acf/save_post. But updating the post means that you are performing some action that alters the database. Altering the HTML output is not updating the database.

    Going back to your original code

    
    function my_acf_save_post($post_id) {
      console_log("Testing...");
    }
    add_action('acf/save_post', 'my_acf_save_post', 20);
    
    function console_log($output, $with_script_tags = true) {
      $js_code = 'console.log(' . json_encode($output, JSON_HEX_TAG) . ')';
    
      if($with_script_tags) {
        $js_code = '<script>' . $js_code . '</script>';
      }
    
      echo $js_code;
    }
    

    This is attempting to create HTML output during the saving of a post.
    Doing this requires 2 steps
    1) During the save process you store some data in some way
    2) During page display you retrieve that stored data and add it to the page

  • Thanks again, John, for your feedback. Yes, this reinforces your prior answer, but if I want both to update a post’s metadata via a post editor window AND update the post itself, how best would I do so?

    For example, it would seem that I need to create a WP core “save_post” add_action, whereby its callback function invokes the ACF “acf/save_post” add_action. Given the latter requires its own callback function, is it a best practice to next an add_action and its callback function within the callback function of another add_action?

    In other words, I understand the purpose of the “save_post” and “acf/save_post” add_actions, but how do I implement them together into a cohesive whole that follows best practices?

  • You cannot do what you are attempting to do with only a server side action run when the post is saved.

    In addition it is the WP save_post action that triggers the acf/save_post action. If you insert or update a post in any way by using wp_insert_post() or wp_update_post() this will trigger will cause ACF to update fields if any ACF fields are submitted. ACF looks at $_POST['acf'] and if it contains anything it save the content to the post ID that was just added or updated. ACF is triggered by the save_post WP hook.

    Sorry, I am at a loss as how I can explain this further.

    Here is an example. Let’s say that I want to trigger a special message about the post being saved.

    In my functions.php file I would add something like

    
    add_action('acf/save_post', 'my_special_message_function', 20);
    function my_special_message_function($post_id) {
      // acf saved the post, set a flag in the DB
      update_post_meta($post_id, 'my_special_message_flag', 1);
    }
    

    Then in my template that outputs the code where this message might appear I add

    
    $flag = get_post_meta($post->ID, 'my_special_message_flag', true);
    if ($flag == 1) {
       // show the special message
       echo 'The Post Was Saved';
       // unset the flag so it is not triggered again
       delete_post_meta($post->ID, 'my_special_message_flag');
    }
    

    The same would be true if you want to determine what the output will be during the save action. The only difference would be that you would have to temporarily store all of the content that you want to be shown, and this would include the entire <script> tag if you wanted to add java script to the page.

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

You must be logged in to reply to this topic.