Support

Account

Home Forums Add-ons Repeater Field update_field() will not remove lines from repeater

Solving

update_field() will not remove lines from repeater

  • I suspect there is a bug with update_field() on Repeater.
    My front end is a React app, that sends a request to delete a line
    by sending the whole array (flat simple rows of text/number fields)
    Over rest API.
    My plugin accepts the request and performs:
    $result = update_field('table_name', $new_table, $cpt_post_id);
    update_field() returns true,
    BUT, when the expected value has less lines – it will NOT REMOVE the missing lines.

    To recreate the issue: (using latest 5.11.4 acf PRO)
    1. Generate a repeater with N lines
    2. Perform update_field() of the whole table with N-1 lines
    It will NOT remove the extra line.

    My work around was to delete the table prior to update field, but due to
    super poor repeater update performance, I am looking for a better solution.

  • Removing a row from a repeater is different to update_field.

    If you check the docs

    You need something like:

    // Delete the first row of data.
    delete_row('images', 1);

    Where 1 is the row number and images if the field name

  • Hi @jarvis
    My focus is on update_field().
    I want an elegant simple & single call without the need to analyze the data in my code.
    Therefore, I expect update_field() to do the work for me.

  • @mulli

    Unfortunately, update_field doesn’t work for deleting rows out of a repeater.

    The delete_row function is also pretty simple!

    As far as I know, it’s the only option if you want to delete content from repeaters

  • Actually, update field if you have the entire repeater correctly nested should delete extra rows. ACF compares the number of rows being saved with the number of rows that existed previously and then deletes the extras after updating.

    How is $new_table formatted?

  • Hi @john
    The $new table is a repeater of “basic” fields, no additional sub components.

    In my tests, the only way to delete row was:
    1. delete the whole table
    2. update_field(with new values)

    I cannot use it becuasse repeater performance is poor.
    An update of a repeater: 15 rows by 27 fields in each row > 40 seconds!
    Unacceptible for my clients.

    PLEASE let me know if update_field (on a repeater) will NOT remove rows.
    I can calculate the diff(oldtable, newtable) and perform “micro-repeater-update-calls’
    But I would like to know before that this is my only workaround.

    THANKS!

  • Hi @jarvis
    Thanks for your answer.
    If you are correct (I believe you do!), there is a bug either in documentation, or in the code.
    Either way, I am looking for an official confirmation, as you can see above.
    To decide on my future actions. I am using repeater massively (more then 100 in a single large app)

    And if this is so, what is the repeater development roadmap for the future?
    Currently, performance update is also an issue.
    I need answers…

  • If you look at ../advanced-custom-fields/pro/fields/class-acf-field-repeater.php line 812 (function update_value) you will see that ACF gets the old number of rows and makes a comparison after updating and then deletes any access rows.

    Performance wise, updating a repeater with 15 rows of 27 fields will not be faster even if you delete everything first, in fact, deleting everything first will reduce performance. Updating his repeater will mean a minimum of 812 queries. Deleting it first will double this to 1624 queries.

    ACF calls update_metadata() for every field, not just once. each sub field on each row of the repeater is stored separately and each must be updated independently.

    More than likely, the reason that the excess rows are not being deleted is that with this number of queries that need to be performed the request simply times out before it can get to that point. It’s likely failing long before it has even done all of the updates.

    There have been many discussions on this forum about the performance of large repeaters.

  • Hi @john
    Thanks for quick super informative answer.
    I did follow the code… upon upadte_field(‘table’,…) ACF simply updates: rewrites
    all
    the table – RENUMBER rows: 1..count(new table).
    Thus, all remaining rows can be deleted.
    Clearly, delete the table prior to update is redundant.

    The algorithm is clear.
    My next test will be with (my) “large” tables – to detect whether there is a timeout.

    BUT, A simple potential optimization on line 844 can be:
    Only if (new_row != old_row)
    Perform $this->update_row( $row, $i, $field, $post_id );

    And you can go even further, treating the case of row renumbering.
    Currently, even row number change will result with compete row rewrite.

    Am I missing something?

  • first, I’m not a developer of ACF, just someone that knows a lot about it.

    When ACF is updating a repeater it does not know what rows have been changed. As you can see by looking at the code it then calls update_row(). Update row in turn calls update update_field() on each field in the row. To not do the update ACF would need to get each value of each sub field and test it against the new value and update if changes. This would not reduce the number of queries. This is exactly what the WP update_metadata() does, if ACF actually did this check itself before calling update_metadata() for a field this would mean a doubling of queries performed when a field needs to be updated.

    What you are missing is that each field is stored independently. Each sub field on each row requires updating independently. There in no flag or any way to know if the value has changed without doing a query of the database to get the existing value and compare them one at a time with the new values. This is an inherent design issue in the way that WP handles the updating of meta fields that has always existed. Any design that has hundreds of custom fields will have performance issues when updating.

  • Hi @john
    Thanks. Your answer is clear.
    Most likely, my acf design can be improved.
    I have an array with 26 flags – can you think of single field
    that will contain the 26 fields?

    The simplest will be text field with format:
    01:DD,02:DD,03:DD – vector of tuples in the form of field-name:field-value.
    Not fun to handle, but replaces 26 fields with a single one.

    Is there a more elegant and cost effective way?

  • Not enough information for me to understand what you’re doing.

    For extremely complex things I generally look beyond ACF and just create my own custom fields. This is easier if I do not need to actually allow the content of the field to be edited in the admin by anyone. For example, just storing my own array under a single field or a single option. This means I simply call update_post_meta() or update_option() or wherever I need to store it and let WP serialize it into the database.

    If I need to allow someone to edit this in the admin I would build a repeater, yes, but I would add an acf/load_value filter on that repeater and populate the rows from my array that I have stored somewhere else. Then I would build an acf/save_post filter that would take the repeater and put it into my array that array and save it. This means that the WP admin for editing would be extremely slow but that other operations are faster. So the biggest question for me would be, does this content need to be editable in the WP admin?

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

You must be logged in to reply to this topic.