Home Forums Bug Reports Flexible Content data still in wp_postmeta after layout removed


Flexible Content data still in wp_postmeta after layout removed

  • I’m running into an issue where a Flexible Content layout’s data remains in wp_postmeta after that layout has been removed from the post. I’ll try to explain the steps required to reproduce this bug:

    Step 1.

    Create a Flexible Content with two layouts. Let’s call the field group “testing”, and the layouts “layout_a” and “layout_b”. Give each layout a text field. These fields should have DIFFERENT names:

    – layout_a: field_a (text)
    – layout_b: field_b (text)

    Step 2.

    Add your Flexible Content field to a post and populate with a “layout_a”, with the field value “value_a”. Save post.

    If you check wp_postmeta you should see a record with the meta key “testing_0_field_a”, the meta_value “value_a” and a post_id matching your post.

    Step 3.

    Edit the post and remove the “layout_a” you created in Step 1 (e.g. click the minus icon next to the layout). At this stage DO NOT save, but instead add a “layout_b” to your layout. Now save.

    In wp_postmeta you should see a lingering record with the meta key “testing_0_field_a”, the meta_value “value_a” and a post_id matching your post. This indicates that the layout was not deleted from the database at Step 3.

    Please note that if you replace a layout with another of the same type it will be deleted correctly. That is because the field names match, and one overwrites the other. e.g. you would be overwriting “testing_0_field_a” with another “testing_0_field_a”.

  • I can confirm this is happening for me too in the latest version (5.2.9). It makes editing the fields really difficult as you have to ‘update’ between each change or risk hidden field data showing up on the site – clients don’t get it at all 🙂 Any news on a fix?

  • This is working the way ACF has always worked. The values from the fields that are removed are not deleted from the database.

    You can probably see this on a repeater field as well, if you add 3 rows and save and then remove one of them the 3rd will probably linger. ACF currently does not have the ability to remove data from fields that don’t exist when saving the form. It only inserts and updates fields that are submitted.

    If you want data that is no longer needed to be deleted you would need to create an acf/save_post action and delete the data yourself.

  • It feels really broken, maybe because I’m more familiar with Repeater fields. When I am editing a Repeater field in a post, and delete one of the repeated-fields, it deletes both the field (from the admin) and the CONTENT of that field. When I add a field back in again, it adds it back in as a blank field. It consistently works this way no matter when I click the main post Update button.

    When I do the same with a Flexible Content row, the row disappears from the admin, but then if I click the ‘+’ button to add a row back in, it appears with all the old content pre-populated. If I delete the row, then Update the post, then add a new row back in, the new row appears blank – but the wp_postmeta from the first deleted row is still in the database and shows up on the front end display (because its contents aren’t blank). And now the only way I can delete the contents of the row I deleted is to go into the database manually and delete them.

    I don’t think this is how it’s supposed to work. Is it?

  • Actually, while you may delete a repeater row and it does not repopulate if you add a new row, that row is still in the database until it gets overwritten, at least that’s my experience.

    When ACF is updating a post it loops through $_POST[‘acf’] and inserts/updated the actual fields submitted. Since your deleted rows are not part of this array ACF is not aware that they exist at this point, which is why they do not get deleted.

    To be honest, I’m not personally concerned about the left over data in the database. I’m not even sure there’s a solution, or at least not an easy solution, other than having your own filter that does the data deletion that is built to work with your fields the way you’ve built them.

    I’ve currently got a site with so many fields on one post type that it times out during a normal save. If ACF attempted to delete all the unused data every time I think this would happen more frequently and put a severe limit on the number and types of fields you could have. This is because every update to post meta created a new DB query. That is an inefficiency in WP core because there isn’t currently a way to delete, update or insert multiple meta values at the same time.

  • I’m not concerned about efficiency, so much as all that “deleted” data showing up on my site pages. Because the data is still in the db, the usual if-not-empty,please-display-it code in my templates shows all those rows. I can’t find any way to say “if deleted (even if not empty) don’t show it”.

  • When you display the data for flex fields you shouldn’t be trying to get fields that aren’t associated with that particular layout.

    $layout = get_row_layout();
    if ($layout == 'layout_1') {
        // show fields associated with layout 1 
    } elseif ($layout == 'layout_2') {
        // show fields associated with layout 2

    Just a basic example, could be far more complicated depending on what you’re doing. For example if 2 layouts have some fields in common then there might be function calls for those groups of fields.

  • I don’t think I am? I started with just one layout, “columns”, displayed like this:

    `// check if the flexible content field has rows of data
    if( have_rows(‘sections’) ) {

    // loop through the rows of data
    while ( have_rows(‘sections’) ) {

    if( get_row_layout() == ‘columns’ ) {

    if (get_sub_field(‘column_1_content’)) {
    echo ‘<div class=”col col-1″>’ . get_sub_field(‘column_1_content’) . ‘</div>’;
    if (get_sub_field(‘column_2_content’)) {
    echo ‘<div class=”col col-2″>’ . get_sub_field(‘column_2_content’) . ‘</div>’;
    if (get_sub_field(‘column_3_content’)) {
    echo ‘<div class=”col col-3″>’ . get_sub_field(‘column_3_content’) . ‘</div>’;
    if (get_sub_field(‘column_4_content’)) {
    echo ‘<div class=”col col-4″>’ . get_sub_field(‘column_4_content’) . ‘</div>’;
    if (get_sub_field(‘column_5_content’)) {
    echo ‘<div class=”col col-5″>’ . get_sub_field(‘column_5_content’) . ‘</div>’;




  • I can’t remember why the undeleted field data bothered me, but I imagine it was the same as Michelle: it was showing up on the front end.

    Certainly it was problematic enough that I opened a bug report.

    In any case, when data is deleted but not removed from the database that feels like a bug to me.

    The way that I imagine you would fix this problem is by deleting the data from wp_postmeta via AJAX when the user deleted the Flexible Content row. I imagine this would be much more efficient than trying to clean it up when the post is saved.

  • I guess this would be more like a feature request. I know that I’ve seen this same topic come up from time to time. Just wanted to let you know that it’s not a bug, or at least that ACF is working the way it’s designed to work. Repeaters, flexible content and even conditional fields require a few more checks in the template files to ensure that you’re only showing what you want to show.

    I can see the argument for both deleting and not deleting the data. For my, most of my clients like to change their minds… a lot, and they don’t want to have to re-enter data every time they do. So having the data not deleted is actually a needed feature for me most of the time.

    Maybe if E could make the data get deleted through AJAX the way WP core does but also having a setting somewhere to disable the feature? Not sure, I’m not the developer. This forum is really only users helping users.

  • John, if your clients’ data isn’t deleted, how do you get it not to show up on the front-end of their sites? I am doing the “not-empty” checks to ensure only fields with data get displayed, but obviously these fields contain content. Is there another check I’m missing that would hide “deleted” fields that still contain content?

  • Like I said above, checking the layout type using get_row_layout() when dealing with flexible content fields and showing content based on that instead of whether or not the field has a value. The use of get_row_layout() is documented here

    When dealing with conditional logic I check that logic by getting the conditional field and only getting the content that is supposed to be displayed.

    I don’t base the display of data on the presence or absence of data in the database. An empty field may mean that I want to display a default value.

    This may be a programmer thing, but I don’t trust any data source to contain what I expect it to contain.

  • John, sorry, I feel like I’m being so dense! My code did have get_row_layout() checks (see above) but was still showing content for the rows/layouts that had been deleted via the admin. What am I missing?

  • What’s the name of the other layout? Is it the same? I just did a test and it seems that ACF will allow you to save 2 layouts with the same name and if you duplicate a layout it will save it with the same name unless you change it. This would also be a problem if your fields have the same name as well.

  • In this scenario, there was only one layout, called “columns”. The ‘columns’ layout contained 5 visual editor fields which were shown/hidden conditionally based on a radio-button field also within the layout.

    I was going to add other layouts, but the issue with all the deleted content showing happened before I could get to that.

  • I just did a quick test.

    I created a flexible content field named 'flex_field'. This field has 2 layouts named 'layout_1' and 'layout_2'.

    I added this code to my template which is a trimmed down version of your code

    if (have_rows('flex_field')) {
      while (have_rows('flex_field')) {
        $layout = get_row_layout();
        if ($layout == 'layout_1') {
          echo '<p>LAYOUT 1</p>';
          echo '<p>',get_sub_field('content_1'),'</p>';
        } elseif ($layout == 'layout_2') {
          echo '<p>LAYOUT 2</p>';
          echo '<p>',get_sub_field('content_2'),'</p>';
    } else {
      echo '<p>NO ROWS TO DISPLAY</p>';

    Before editing the post I viewed it on the front then and the page displayed NO ROWS TO DISPLAY as expected. I added a layout_1 row and loaded the page. It displayed LAYOUT 1 as expected. I then deleted the layout row and added a layout_2 row. Layout 2 content was shown. I deleted the layout 2 row and once again NO ROWS TO DISPLAY was shown on the front page.

    I’ve looked at your code again and I don’t see anything wrong with it. If you are seeing content on the front end that should not be displayed then the problem is not with ACF as far as I can see. Try disabling other plugins on the site. Recheck the content that is added to your fields. Maybe there is some other plugin that is interfering with the updating of the field.

    While the data does stick around in the database, if the front end code is correct this content should not be displayed.

  • I can confirm that when following the steps in my original post, no ghost data is displayed on the front end within a regular while (have_rows) loop.

    I wish I had mentioned in my original post what steps to take to get the ghost data to display on the front end, but six months later I’m afraid can’t remember!

    Perhaps, as John suggests, there was some other factor at play. Perhaps I was accessing data outside an ACF loop. Or maybe the behaviour of ACF has changed since February to fix my initial problem.

    Hopefully Michelle’s testing will be able to shed some light.

  • Okay, I think I’ve narrowed down the bug.

    In my original ‘Columns’ layout I had a radio button so people could select the column layout they wanted, for example “100%” or “33% / 66%” or “33% / 33% / 33%”.

    I set up 3 WYSIWYG editors and used conditionals to hide/show them in the editing panel based on what the user selected. So if you chose “100%” you’d see just one editor (this is the default), if you chose “33/66” you’d see two editors, and 3 editors for 33/33/33.

    So here are the steps to recreate the issue.

    1. Add a new ‘column’ layout on a page, select ’33/66′ and enter content in the two visible editor panels.
    2. Add another column’ layout, select ’33/33/33′ and enter content in the three editor panels.
    3. Save/update the page, you now have 2 column layouts displaying, the first with 2 columns, the second with 3.
    4. Click the (-) symbol to delete the 33/33/33 layout. WITHOUT SAVING, add another ‘column’ layout in the same spot. By default it shows with one 100% editor showing, and it’s blank. Looks like a totally empty row. Choose 33/66 – both still blank – same for 33/33/33. Now switch back to 100% and the single editor, enter a piece of content, and “Update” the page.
    5. Now look at the front end – your second column layout will show your new content in the first column/editor space, and the old content (that you thought was deleted) in the other two spots – still 3 columns. It’s like a magic trick. 🙂

    So, the “fix” is to delete all content out of all three Editors, save the page, then delete the layout, then save the page again – every time you want to delete a layout row. If you don’t do that, and get too many layout addition/deletions down the road, it becomes impossible to find the content that’s saved in the db because it doesn’t appear in the WYSIWYG editors when you add new layout rows.

    Or is it just me? 🙂 I just updated to the most recent ACF Pro though I’m not on 4.3 yet.

  • So, the problem here isn’t that the layout stays in the db, the problem is that you need to check these conditional fields for more than if they have content or not. This code is not exactly what you’ll need to use in your case but it should give you an idea.

    $condition = get_field('your_condition_field');
    if ($condition == '100') {
      if (get_field('content_1')) {
        // do stuff to output content 1
    if ($condition == '33/66') {
      if (get_field('content_2')) {
        // do stuff to output content 2
    if ($condition == '33/33/33') {
      if (get_field('content_3')) {
        // do stuff to output content 3
  • John you are totally correct, that would solve it. THANK YOU! It’s been a long hard week – you’re so kind to have stuck with me through all that. Thank you!

  • I’ve had some long hard weeks in my time. Having one now actually. Helping out here is a nice diversion from the things that are currently frustrating me 🙂 Happy to help where I can.

  • Although I can’t remember precisely what problem I was having when I opened this thread, I have marked it as solved. Good work John!

  • As of ACF Pro 5.5.8, the flexible fields clean up after themselves when they are deleted, so no more ghost data in the postmeta table!

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

The topic ‘Flexible Content data still in wp_postmeta after layout removed’ is closed to new replies.