Support

Account

Forum Replies Created

  • 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!

  • @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.

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