Ok, I’ve run into another issue with this and really think there needs to be an official way to do this. Not only is there no way to add styles to the actual block element, there’s also no way to add classes. The end result of this is that when working with inner blocks you effectively cannot make the frontend and backend match.
I’d expect to have to use some different styles on the backend to support editing, but as is you effectively have to style everything twice, and if you have dynamically added classes those are completely impossible to style correctly.
We need some mechanism of adding styles and classes (possibly other attributes) to the .wp-block element for our blocks using the values from get_field().
Here’s a thread where I worked out a way to do this for specific fields with John’s help: https://support.advancedcustomfields.com/forums/topic/pruning-orphaned-flex-field-data/
If your fields are all child fields of a single flex/repeater/group, or share some common prefix, etc, then you could apply this to clean up the database each time a post is saved.
ok, I found a really janky way to do this, but it works. Open to ideas for improvements:
In JS you can use the blocks.registerBlockType filter to add a getEditWrapperProps function to all blocks (or just the ones you need). Ex:
wp.hooks.addFilter(
'blocks.registerBlockType',
'plugin/add-acf-ids',
function ( settings, name ) {
return lodash.assign( {}, settings, {
getEditWrapperProps: (props) => {
return {'data-acfid': props.id}
},
});
}
);
Then in my renderBlock function I have something like this:
$previewStyles = "";
if($isPreview) {
$previewStyles = "<style>#editor .wp-block[data-acfid='{$acf['id']}'] {{$styles}}</style>";
$styles = "";
}
$preBlock = $this->filter("Block/$slug/start", "<{$tag} class='aesir-builder__block {$slug} {$classes}' style='{$styles}'>\n{$previewStyles}", $block, $classes, $styles, $previewStyles, $slug, $tag, $content, $isPreview, $postID);
That’s boilerplate wrapped around all my blocks, so I provide $styles from a class that defines my block, and it gets translated into either inline styles for frontend, or a style tag for the editor.
I hate everything about this, but at least it’s kinda working.
Just saw this, here’s what I ended up with – it’s nearly identical to the above:
public static function remove_orphans($post_id) {
global $wpdb;
$wpdb->query($wpdb->prepare("
DELETE FROM {$wpdb->postmeta}
WHERE {$wpdb->postmeta}.post_id = %d
AND (
meta_key LIKE '".static::PAGE_BUILDER_ACF_FIELD."_%'
OR meta_key LIKE '_".static::PAGE_BUILDER_ACF_FIELD."_%'
)",
$post_id
));
wp_cache_flush();
}
I’ve now tested this pretty extensively, having re-saved all the pages on a nearly 100 page site with no issues. This shaved a little over 60mb out of the postmeta table on this site.
Its worth noting that this does also clear out any saved data that is hidden behind conditional logic. I consider this a feature personally, as I find the default behavior of conditional fields in ACF a bit weird and error prone. This prevents situations where a field that is hidden by logic can still have a value even if its not visible.
I need to do some additional testing (due to how destructive this operation can be), but initial testing seems to indicate that running wp_cache_flush() immediately after the deletion does indeed fix the problem.
Thanks a bunch, hopefully that’s all it needed!
Thanks John, that is very helpful.
Here’s the entirety of the function, its set to priority 0:
public static function remove_orphans($post_id) {
global $wpdb;
$result = $wpdb->query($wpdb->prepare("
DELETE FROM {$wpdb->postmeta}
WHERE {$wpdb->postmeta}.post_id = %d
AND (
meta_key LIKE '".static::PAGE_BUILDER_ACF_FIELD."_%'
OR meta_key LIKE '_".static::PAGE_BUILDER_ACF_FIELD."_%'
)",
$post_id
));
}
It’s very straightforward, hence my confusion.
The cache is interesting, I will give that a shot in a little bit and let you know if it works… Based on your description that does seem like it could be the source of some odd behavior.
I don’t think it will be an issue in normal operation if this process gets skipped when no changes are made, but I would like to be able to force it so that I don’t have to go through everything and make arbitrary changes to trigger an update – do you know of an easy way to do so and/or what’s the name of the field ACF uses to track that?
Thanks!
Nevermind – figured it out, the theme itself was including Select2 on the Admin as part of another library. Apparently it must have been compatible in some way up until recently.
The particular limitation is listed in the documentation:
“2. have_rows() nested clone field
The have_rows() function will return false when loading a cloned sub field using the ‘Display = Block’ setting. Please note that using the ‘Seamless’ display setting will allow sub have_rows() loops to work as expected.”
Unfortunately the Seamless display mode doesn’t work for my purposes, so I’m stuck with grouped. I know Elliot has mentioned trying to remove some of these limitations in the past, so this is in part intended to be a suggestion on how he might accomplish that. Beyond that though I can think of some non-clone field applications where such a thing might be handy.
I’ve already written replacements for the various ACF functions for my own use case, but I’d prefer to be able to use the standard ACF code for consistency.
Hi Elliot, since I see you were planning to work on this issue after 5.5.0 I thought I’d bump this to see if there’s been any progress?
I know personally it’d be amazing if we could treat grouped Clone fields as 1-row repeaters. That would solve a number of problems with my implementation.
I’d like to help as much as possible, and will spread word of your effort. Time is (as you say) a major constraint, but I’ll do what I can. I think this issue is a real limiter on the usefulness of ACF going forward, and I’ve already had to make design decisions based around it on several occasions.
Hi Elliot,
I just tested this against my two example files and both worked – this seems to have fixed the issue. I did not test if a field could recursively copy its container field without an intermediary clone field in a different group, as I consider it a minor inconvenience if that’s still not possible – I suspect you probably fixed that as well though.
Thanks!
I looked into this some today, it looks like it might be possible to create a new class to replace acf_input, then reassign acf()->input to use the new class. This should allow you to replace acf_input::save_post().
This is more invasive and hacky than I’d like, but it might work. It would be better to just have this functionality built into the core, of course. I wonder if Elliot would consider it as a feature request?
Of course it already exists, Elliot really needs to charge more!
I’m dealing with a site right now that has a lot of fields and I’ve been thinking about this issue. I’m using ACF as a page builder of sorts, and while I’ve found ways to reduce the number of fields in use the slowdown is noticeable on large pages.
I’m wondering if some sort of alternate saving option might be viable? For example, if top-level repeaters and flexible content fields were saved as a single post meta (encoded in some format), then only decoded and cached when have_rows() is called, that might help with this sort of issue (I assume all large field quantity issues come from repeaters and flex fields). I am not at all sure what other repercussions this might have though, nor if there are better options. If there are tradeoffs, making it an option on the field group level might be sufficient so that we can choose the best save method for our intended use case.
Hi Elliot,
Many thanks! I will go ahead and mark this as solved.
Yeah, it doesn’t help that I originally thought that this was an on-save bug due to my mangled JSON files (which I now realize were happening after saving the field groups without noticing that the seemless fields had been replaced).
Please also convey my request for a the_row() equivalent for grouped fields – it would be enormously helpful. Thank you for your help and sorry for the confusion.
I see that someone else is also having issues similar to this, and that it may be related to importing via the menu vs syncing from the acf-json folder. If you are importing manually vs syncing that may explain why you aren’t having these issues (I always sync, and am depending on the sync functionality for what I’m trying to do).
If you try this again make sure to use the sync feature rather than the manual import, I assumed they worked the same but it seems like that might not be the case.
Tomorrow I’ll see if I can record a screencast of what I am seeing, this is 100% reproducible on my end (I’ve tried it on three different WP installs, and rebuilt the fields multiple times). The complex example should import/sync with 0 fields, while the simple one should work as expected except that the clone field should be imported into the main field group as if it wasn’t a clone field (ie, you can edit clone field group’s fields inside the main field and no clone field is present in the main field, but if you look at the JSON it should be a clone field and was created as a clone field, also editing these fields is buggy and reordering them does not save).
I’m not sure if I’m not explaining it adequately or if you are not experiencing the same issue I am.
Were you able to use the above info to replicate the problem?
Ah, I did actually look at those but must have had a mental hiccup – I could have sworn it was using has_sub_field() to check for a field’s presence.
Problem solved, not a bug.
Thanks!
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.