Home › Forums › General Issues › acf/save_post not working with calculated update_field
I’m working on building a complex proposal system and really could use some advice on getting past this hurdle. We have a main acf group with questions that the user answers via acf_form. The form is used to create a new post. Upon clicking the submit button to create this new post all sorts of code is executed, but the two main calls are:
/* Load Defaults from options page and user page*/
function update_save($post_id) {
$post_type = get_post_type($post_id); // Get Post Type From POST ID
if ( get_post_status ( $post_id ) == 'draft' || get_post_status ( $post_id ) == 'pending' ) { // Check to see if draft or pending
$fields = get_fields($post_id); // Load ACF Fields connected to this POST ID
$post_author_id = 'user_'.get_post_field( 'post_author', $post_id );
if( $fields ) // Check if this post has ACF fields
{
foreach( $fields as $field_name => $value ) //Separate fields out using field name as array variable
{
$field = get_field_object($field_name, false, array('load_value' => false)); // Get the rest of the array of field objects
$replace = get_field($field_name, $post_id); // Get current value of variable
$default_value = get_field($field_name, $post_author_id); // Get the default value set on options page, if there is none, look on the author page
if(empty($default_value)){
$default_value = get_field($field_name, 'options');
}
if(empty($replace)) { // Check if $replace has a value
update_field($field_name, $default_value, $post_id); // Updates the value
}
}
}
} else {
}
}
add_action('acf/save_post', 'update_save', 50); //Trigger after every save containing ACF fields
/* Makes calculations based on user input on acf_form and the defaults loaded above */
function acf_load_total( $post_id ) {
/*Calculations are made, example:*/
$field_data1 = $proposal_input * $system_default;
update_field('field_key1', $field_data1, $post_id);
update_field('field_key2', $field_data2, $post_id);
update_field('field_key3', $field_data3, $post_id);
update_field('field_key4', $field_data4, $post_id);
...etc
}
add_action('acf/save_post', 'acf_load_total', 100);
All of the fields are populating correctly in the first set of code (where the blank fields are being updated with the system defaults) when the post is created. However the second set of code where the calculations occur do not calculate/save as expected. If I manually save the post after the initial creation, then the second set of code works.
My goal is to have the calculated fields work/save on the initial post creation (as well as subsequent saves).
I have tried using both the field name and the field key to no avail. I was thinking maybe it wasn’t working since the calculations are using data in the fields that were loaded in the first set of code, and perhaps the data isn’t available yet for the second set of code when it was executed. However, this shouldn’t be the case, since the acf_load_total function is run with a higher acf/save_post priority, so it should run after the defaults are loaded.
The code is working, I just have to save the post after it is initially created. After that, it works as expected. Can anyone help me figure out how to avoid doing this extra step?
Maybe you didn’t include all the code for the second part. Where are the values $proposal_input
and $system_default
coming from in the function acf_load_total
The bulk of the second part was not included because it is very verbose, using basic math functions. Essentially the formulas use data that was copied into the post via the first block of code AND also data that was generated by the user and saved when the post was created. I just put those variables there as an example, but not referencing anything specific. All my data is stored across 100+ ACF fields in the post itself.
I just ran a test and found the function is working, but not all of my fields are available when the second set of code it executed, as I feared. Even setting the priority to 999 does not help, even though it is for sure the last thing executed. Shouldn’t the fields that were updated in part one be available for the code in part two to use?
Since it’s the second function that you’re having a problem with…
Without seeing it all, and I’m not sure I want to if you’re dealing with 100+ fields, here are some suggestions about things that might be wrong, and some of this might be obvious.
Make sure that you’re getting the values you need to manipulate from the DB. So if $proposal_input
is dependent on a value in another field then you need to do $proposal_input = get_field('proposal_input', $post_id);
. Values retrieved in the first function will not be available in the second function, you need to get them again.
Are these fields that have already been created and that already exist. If you are attempting to update a field that does not already have a value in the database then using update_field() with the field name will fail, you must use the field_key.
It’s possible that the old values are not being cleared from the cache when they are updated. You could try clearing the cache at the beginning of the second function https://codex.wordpress.org/Function_Reference/clean_post_cache
On the backend of the site we have ACF Fields/Groups, and the groups are assigned to show on these posts. The fields have no default value defined in ACF, we define the values by the first code block, which copies the data from an options page and the author page. We do this because each post needs to stay the same in the future, even if the data on the options page changes.
The issue occurs when making a new post (either through the backend or acf_form frontend). So we don’t manually input any data in these fields, but they are updated programmatically when creating the post. I am already defining each variable with get_field($field_key,$post_id) as mentioned above.
I have found there are some fields that just aren’t able to get_field() when the calculation is made the first time, and that is what is causing the issue. Once the save is completed the then empty fields are defined, so saving again allows the calculation to process correctly.
I assumed that since the calculations (code block 2) were happening after the fields are defined (code block 1), then the calculations should be able to pull that data. Is this a bug? If not, then what’s the purpose of having acf/save_post priorities if fields aren’t updated in the order your would expect?
Can you think of any methods where I can make this data available? If there are not any clean methods, is there anything “hacky” you could think of, like mimicking/forcing a post to save twice programmatically on it’s initial creation (the first save)?
I’ve just looked back over your code and it is a problem in the first function.
get_fields($post_id);
will only return fields that have already been created in the database. So if there are fields that are not set when the post is initially created, they will not exist and will not have values. This means that $replace = get_field($field_name, $post_id);
will never run on a field that does not already exist in the DB so if(empty($default_value)){
is never true.
I’ll look into how you can work around this….
So, you have a couple of choices, the first is to construct an array with all of the fields that you want to check and update. This array will need to look something like this so that you have field names and keys, you will need the field keys for updating the field.
$fields = array(
'field_name' => 'field_key',
'field_name' => 'field_key',
'field_name' => 'field_key',
'field_name' => 'field_key',
'field_name' => 'field_key'
)
then you can loop through this array and test for values, get values and update values.
or you can do something like this, see comments about what’s going on
/* Load Defaults from options page and user page*/
function update_save($post_id) {
// Get Post Type From POST ID
$post_type = get_post_type($post_id);
// you should check the post type is your post type and return if not
if ($post_type != 'your-post-type') {
return;
}
// Check to see if draft or pending
if (get_post_status ( $post_id ) == 'draft' || get_post_status ( $post_id ) == 'pending') {
$post_author_id = 'user_'.get_post_field( 'post_author', $post_id );
// acf_get_fields() is an internal ACF funciton
// that will return all fields in a field group
// repeater fields will be nested in $field['sub_fields']
// this is the only way to get a list of all of the fields
// if some of them do not have values
$group_key = 'group_57007d5b46c49';
$fields = acf_get_fields($group_key);
foreach ($fields as $field) {
$replace = get_field($field['name'], $post_id);
if (!empty($relace)) {
// field has a value
// go to the next field
continue;
}
$default_value = get_field($field['name'], $post_author_id);
if (empty($default_value)) {
$default_value = get_field($field['name'], 'options');
}
update_field($field['key'], $default_value);
}
} else {
}
}
add_action('acf/save_post', 'update_save', 50); //Trigger after every save containing ACF fields
let me know if you have any questions.
Is the first option a more manual version of the second one, I didn’t quite follow where you were going with it.
As for the second one, it is very close to what I had, but with some better checks and using acf_get_fields, instead of get_fields. What is the reasoning behind this, won’t get fields work just as well?
I have 4 acf groups and I include them in acf_form from the beginning:
/* Create Front-End Form */
global $current_user; //get current logged in user info
$author_id = $current_user->ID; //pull user id and assign to author_id variable
//form/post options
$args = array(
'post_id' => 'new_post', //creates new post instead of editing
'field_groups' => array(79,359,1877,1966), //proposal field group
'return' => '%post_url%', // redirect to new post url
'new_post' => array( //option for new post
'post_type' => 'proposals', //creates a new post in "Proposals" CPT
'post_status' => 'draft', //
'post_author' => $author_id //assigns author as the person logged in
),
'submit_value' => 'Create Proposal'
);
//loads the ACF fields with above settings
acf_form( $args );
I tried the second option, but I got the same outcome. All of the fields populate on creation except for the calculations, since they can’t get the values for the other fields.
Like I said in the post before the last one, get_fields() will only get fields that have values, that’s why your first function is not setting the defaults you want. See that comment for more information.
Yes. creating an array in the first example would be more manual where you list exactly what rows you want to check. If you did it the first way you could eliminate getting values for fields you don’t need to look at an concentrate entire on the setting defaults part.
When I open the frontend page with the ACF Form, then click the submit button, a new post is created. If I go on the backend to that post, there are ACF fields with data copied over to them the way I would expect. This part is all fine and good. I tried the two suggestions you made, and there was no difference between the the results of what I was doing and the two methods you suggested, unfortunately.
I’m not sure if I’m being really dense and not getting your point, but I’ve been through the forums and resources 100x times, I could recite the content of the acf/save_post and update_field.
During the “saving” process, the fields I copy over (via the first block of code in the first post) are not accessible to the code in the second set of code; however after the “saving” process is completed and the page loads up, the fields are there. I’m trying to understand why the calculations actions that are scheduled (priority 50) to occur after the fields are filled in (priority 30) aren’t working. I either need to figure out why the priorities aren’t working like I’d expect, or an action to execute after a post save, or a hack to force the post to save twice.
I don’t know of any reason why the fields are not available in the second function. The priorities should be working as you expect them to. I still think that some of the fields are not being saved during the first function and I still think that if you’re using the first function as you originally posted that my previous comment still applies.
I’ve just looked back over your code and it is a problem in the first function.
get_fields($post_id);
will only return fields that have already been created in the database. So if there are fields that are not set when the post is initially created, they will not exist and will not have values. This means that$replace = get_field($field_name, $post_id);
will never run on a field that does not already exist in the DB soif(empty($default_value)){
is never true.
At this point I thing you should submit a support ticket and see if they can help you figure it out https://support.advancedcustomfields.com/new-ticket/
I have tried your methods, and they didn’t resolve this issue. And the fields are populating, because when I go the back end, they are there. I appreciate your time and effort John, I really do. I’ll probably submit a ticket like you suggest.
In the meantime I have figured out a temporary solution. Instead of attaching the calculations to run during the save process (acf/save_post) I have just place a call to the function right in my template at the top of the page. This is giving me the results I had expected – the function runs, updates fields, then the page loads, where I can use get_field to display the results of the calculations.
The topic ‘acf/save_post not working with calculated update_field’ is closed to new replies.
Welcome to the Advanced Custom Fields community forum.
Browse through ideas, snippets of code, questions and answers between fellow ACF users
Helping others is a great way to earn karma, gain badges and help ACF development!
We use cookies to offer you a better browsing experience, analyze site traffic and personalize content. Read about how we use cookies and how you can control them in our Privacy Policy. If you continue to use this site, you consent to our use of cookies.