Support

Account

Home Forums ACF PRO Updating Field Settings in PHP

Solved

Updating Field Settings in PHP

  • Is there a way to programatically change the settings of a field in PHP? I have been scouring the documentation, and I have been unable to find any information on saving new field settings for an existing field using the available PHP functions.

    For example, let’s say I have a Select field that has a handful of choices already, and this field is associated with a specific post type. There are already a bunch of posts that have their own different values selected for this field. Now, I would like to run a PHP function that appends a new choice to that Select field and saves it in the database, so that any time in the future when I add or edit a post that has access to this field, the new choice appears as an option in the dropdown. This would be the equivalent of going into the admin dashboard, opening the field group, finding the field in question, adding a new line in the “Choices” textarea, and clicking “Update,” but I need to do it using a PHP script.

    This is only one example. Others could include updating the “Instructions” for that field, or the label. Is it possible to edit any settings for an existing field and save them using a PHP script — without using the GUI in the admin dashboard?

    Any help will be greatly appreciated!

  • Updating choices for a field, or any other information can be done using the acf_update_field() function. This function is not documented, but it is the function that ACF calls internally to update a field when it’s changed in the admin.

    
    // get the field... use the field key
    $field = get_field_object('field_1234567');
    
    // modify the settings of the field
    $field['choices']['new-choice-value'] = 'new choice label';
    
    // tell ACF to update the field settings
    acf_update_field($field);
    

    There are other ways to accomplish some other things that might be a better field. Filters on acf/load_field https://www.advancedcustomfields.com/resources/acf-load_field/ and acf/prepare_field https://www.advancedcustomfields.com/resources/acf-prepare_field/ can be use to temporarily alter values for display.

  • @hube2, thank you for your response. The knowledge that there are helpful but undocumented functions sent me on the right track. While your answer does cover the basics, it does not fully solve the problem. I spent about 8 hours yesterday digging through the source code and testing different things until I finally cracked it. For anyone else out there who may come across this, here are the details:

    The Problems

    // get the field... use the field key
    $field = get_field_object('field_1234567');

    While the get_field_object() object retrieves most of the field data when using the field key as the argument, it notably does NOT retrieve the post ID (it is instead set to 0). Additionally, it uses the group field key as the value for the “parent” element instead of its post ID. Both of these are relevant because…

    // tell ACF to update the field settings
    acf_update_field($field);

    The acf_update_field() function has some internal logic that ONLY updates the existing field in the database if the post ID is present in the $field argument. If it is not present, it instead inserts a new post. To the best of my knowledge, the new post it inserts in this instance is entirely useless for a couple reasons. First, it does not have the proper parent ID that allows WordPress to associate it with its field group. Secondly, since there is already a database entry for this field that DOES have the parent ID, this new post never gets used for anything and only takes up space in your table.

    The Solution

    This is the sequence of events that I found to properly update the settings of a field in PHP. I am not an ACF expert by any means, so it’s possible that there is a more efficient way.

    1. Retrieve the proper field object including the post ID.

      Luckily, ACF has a built-in function to do just that. acf_get_field($selector = null, $db_only = false) will return a fully populated field array as long as we pass true as the second argument.

      $field = acf_get_field($field_key, true);

      In addition to having the proper post ID in the “ID” element, it also now has the post ID of the field group in the “parent” element.

    2. Update your desired field settings.

      Simply change the value of the array element that you wish to update. In my case, I appended a new entry to the “choices” element with $field['choices']['new-choice-value'] = ['new-choice-label'].

      Theoretically this should work the same with any of the settings you’d like to change, such as the instructions ($field['instructions'] = 'These are my new instructions.').

    3. Update the field in the database.

      Now that we have the correct post ID included in the $field argument, the acf_update_field() properly updates the database entry for our field.

      acf_update_field($field);

    4. We’re done, right? Wrong.

      Now, if you are like me, you may have expected this to be the end. Unfortunately, we’re only about halfway done. I’ll explain:

      After running acf_update_field(), the field data is indeed updated in the database. When you visit/refresh the page to edit the field group in the admin dashboard, you will even see the new settings reflected there. However, if you visit a page where you can set the value for this field (such as an Options Page), the new settings will NOT be visible there.

      In my example, the new choice I added does appear when editing the field group, but it does NOT appear on the options page (only the choices that were previously there prior to running the steps above are available). This means that the new choice isn’t actually usable in any meaningful way.

      Since I am not an ACF expert, it took me quite a long time to understand what was happening, but I eventually found the culprit…

      The JSON File

      If you aren’t aware, ACF5 creates and references local JSON files for your field groups in an effort to reduce the number of database calls it has to make when loading fields and field groups. By default, these JSON files are stored in /wp-content/your-theme/acf-json and are named using the group field key (e.g. group_1234567.json). You can read a bit more about the JSON feature here: https://www.advancedcustomfields.com/resources/local-json/.

      As it turns out, when editing the field group settings in the admin dashboard, ACF loads the data from the database. When it is loading fields for use in other areas (such as the associated Options Page), it loads the data from the JSON file if it is available.

      acf_update_field() does NOT update the JSON file. I’ll leave it up to the devs to determine whether this is intended or not, but in my opinion, it seems strange to have a function designed to update data that ends up creating a data discrepancy.

      This is why we must continue to the next steps. We must update the JSON file for the entire field group.

    5. Retrieve the field group data.

      Luckily, we already have the post ID of the field group (a.k.a. the parent of the field we’re updating) from step 1, we can use the acf_get_field_group() function to retrieve it.

      acf_get_field_group($field['parent']);

      This function retrieves the field group array, but the “fields” element is empty. In order to properly update the JSON file, we must populate it with all of the necessary field data — including our field with updated settings.

    6. Add all field data to the field group.

      It’s worth mentioning that there’s a function update_field_group() that takes a field group argument, tries to add all of its field data, and then write the JSON file, but I found that the functions it uses to get the field data will always prioritize retrieving from the JSON file instead of the database, so it won’t ever get our field with the new settings because — as I mentioned in step 4 — our new field settings aren’t in the JSON file yet. Because of this, we must do some work on our own to put the proper field data in.

      First, we will add all of the OLD field data to the field group, and only after this will we put the one with the new settings in. To get the old field data, we use the acf_get_fields() function.

      $field_group['fields'] = acf_get_fields($field_group);

      Note that there are a few other functions available here that can be used to retrieve the field data for the field group, but they all appear to return the old data. One of them (acf_get_fields_by_id($parent_id)) even goes so far as to run a get_posts() query to retrieve all of the associated field posts from the database, but then it only uses it to compile an array of post IDs which it loops through to fetch each field individually, again prioritizing the old JSON file data first.

      Now that we have the OLD field data in our field group array, we need to replace one of the elements with our new field (the one with the new settings). I looped through the fields until I found the one with a matching field key and replaced it.

      `
      foreach ($field_group[‘fields’] as $i => $json_field) {
      if ($json_field[‘key’] == $field[‘key’]) {
      $field_group[‘fields’][$i] = $field;
      break;
      }
      }
      `

      Which brings us to our last step!

    7. Write the new JSON file.

      This step is straightforward. Run the acf_write_json_field_group() function with our new field group data.

      acf_write_json_field_group($field_group);

      If everything went as expected, your field will now have its new settings reflected everywhere.

    Now, the main thing I learned here is that the JSON feature probably has a bit of work that could be done. In most scenarios, ACF prioritizes reading field and field group data from the local JSON file, but there are still some places where that is not the case. The Edit Field Group page in the admin dashboard, for example, reads from the database and overwrites the JSON file when updated. And the acf_update_field() function doesn’t modify the JSON file to reflect any changes made, thus the field hasn’t been updated in any usable way yet. It’s not clear to me whether there’s supposed to be a master/slave relationship between the database and the JSON file to know which has priority, either globally or situationally. It’s likely that I need to look into the JSON synchronization functions to gain more understanding, but for now my particular problem has been solved.

    If anyone has any ideas or feedback on ways to improve the process above, I would love to hear about it.

  • I was able to remove your duplicate.

    I’m actually using my suggestion on a site without any difficulties, for many fields. I needed to give a client the ability to modify field choices without them needing to understand how to make those changes using ACF so I created an options page and when the options page is saved all of the fields with choices are updated. I’m not having an issue with your first item dealing with the ID. I just use the code I included.

    However, I am not using local json on this site and I never needed to work through this particular problem. More than likely, your problem with not having the field ID is also due to the existence of the json files because the field ID is not included there and that’s where you were loading the field from. I will file the email from this away for future reference. I kinda agree that calling acf_update_field should trigger the re-saving of the json file, but I’m not the dev either. I have not dug that deep into the update process that ACF uses, more then likely though that happens later in the field group saving process after the fields are all updated.

    As far as the undocumented functions go, yes there are a few, but the average ACF user will never need them. I’ve spent many hours digging through ACF code and I’m always glad that it’s well written not not over-complicated. You can almost always find a way to do something, but not always. There are things that just can’t be done and they usually have more to do with what can be done in WP and not specific to ACF.

  • As for the JSON loading. JSON files always have priority over the database except for the ACF field group edit page. There is syncing from the field group to the DB if you do that manually, but there is not syncing in the other direction.

    You could reduce some of the work that your doing by first updating the JSON file and then calling the acf function that does the syncing and make ACF update the DB. I don’t recall the function to use here. but I did find this that will lead you in that direction https://gist.github.com/webgurus/6c92ca9c4f660abc06ec

  • John,

    I appreciate the insight. As I work more closely with ACF, I will consider the relationship with the JSON files and the database to try to streamline the process. I’ll also investigate what steps are necessary to avoid using the JSON files altogether like your example, and what side effects that may have.

    I agree that most users won’t run into these sorts of issues, so I’m glad a workaround was possible at all.

    Keep up the good work!

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

The topic ‘Updating Field Settings in PHP’ is closed to new replies.