Home › Forums › Add-ons › Flexible Content Field › Populate (same) flexible content layouts with data from other fields of CPT?
The starting point for this is a custom post type. This post type has a field group with a set of fields of different types. Most of them are text, taxonomies, images and such. There are a few repeater fields (with one sub field – text). There are no flexible content fields. There is currently about 40 fields in the post type.
The fields render in the single view of the post type, which is a static template.
However, the single view will be rebuilt, based on flexible content layouts instead. The flexible content field is in an additional field group that will be added to the post type.
Instead of moving all the content manually, it would be ideal to automatically populate the flexible content with some of the old data, with the help of a script.
To start, the posts will have exactly the same layouts, in exactly the same order. So each post will look the same at this point.
The idea is that
We currently have about 130 posts of this post type. This would mean LOTS of manual work without a script.
When all the “old” data has been scripted to the layouts, the plan is that the administrator THEN can rearrange the layouts, and add more. So this is a “one time action”.
Any tips on how this could be achieved?
There is no “easy” way to do this. If I had to do this then then I would.
1) Create my new flexible content fields
2) Add an acf/load_value filter on the flexible content field
// field_XXXXXXX represents the field key of the flex field
add_filter('acf/load_value/key=field_XXXXXXX', 'convert_to_flex' 20, 3);
function convert_to_flex($value, $post_id, $field) {
// only run in the flex field is empty
if (!empty($value)) {
return $value;
}
// construct flex layouts
return $value;
}
// construct flex layouts
in the above would be replaced code that builds the layouts from the old fields.
$value
is a nested array containing each layout
$value = array(
// nested array for each layout
array(
// acf layout name
'acf_fc_layout' => 'LAYOUT NAME',
// field_key => value pairs of each field in layout
'field_XXXXXXX' => get_field('field_name, $post_id, false'),
'field_YYYYYYY' => get_field('another_field_name', $post_id, false)
)
);
note that we are using the 3rd parameter (false) so that the values are not formatted.
If your layout contains repeater fields then you would have to loop over the repeater and build another nested array, the array for a repeater is similar to the array for a flex field.
// repeater
'field_ZZZZZZZ' => array(
// nested repeater for each row
array(
// field_key => value pairs for each sub field
)
)
What the above does is that when a post is edited the content will be transferred to the new fields.
3) Once this is done and I was sure it was working is that I would rebuild my templates but I would leave the old code alone. I would add a check to see if the new layout fields have a value. They should, but you never know.
if (have_rows('new_flex_field')) {
// new code to show flex fields
} else {
// old code for old fields
}
4) Once I had that done I would then hide all of the original fields using acf/prepare_field filters. I would have an array of all of the old field keys and add a filter for each (DO NOT DELETE THESE FIELDS).
$fields = array(
'field_AAAAAAA',
'field_BBBBBBB',
// etc
);
foreach ($fields as $field) {
add_filter('acf/prepare_field/key='.$field, '__return_false', 20);
);
5) As the final step I would create an acf/save_post action that deletes all of the old fields using delete_field() calls.
When you’re done you have a system that will create the new layouts when each post is updated but allow the site and unedited posts to continue running as is, avoiding the need to update every post. At this point I would ignore it and let it run. At some point in the future, once all posts have been converted you might consider deleting the old fields and the old code, but I probably would not look at it again.
Thank you so much for this detailed answer! It’s really helpful!
I have one follow up question though – concerning the “construct layout” step and I would be really grateful if you could explain it a bit further. Does it mean that I build the content of $flex_field within the post first, and then dump the $flex_field? I suppose that gives me a nested array with all the layouts in order?
Thanks again!
When ACF initially loads the value of the flex field, if it is populated, that value will an array with nested arrays for each row in the layout. What you will do is use the existing fields to build out all the layouts and return it. This will cause ACF do display all of the content you add at this point.
I don’t know what you mean by dumping it. What you are doing is replacing what ACF has loaded and the filter with the priority I gave runs after the value is laded and before ACF shows the flex field. This filter will (should) also run on the front end on the when you do if(have_rows(....
because this is when ACF loads the values. This happens between ACF loading the value and ACF formatting the values, this is why we want to get the existing fields without formatting.
In reality, with this in place you should not need to keep the old code. However, I would keep the old code and do the if/else check in the template anyway. This will not cost you anything and it is a good practice to double check and not assume.
You must be logged in to reply to this topic.
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.