Prenatal ACF Data Insertion (...or "that's the most boring title I've ever written.")

2020-02-02 - Reading time: 9 minutes

Just wanted to document an interesting issue I had with Advanced Custom Fields recently...

Don't get me wrong, I swear by ACF on all of my WordPress projects. I consider it an absolute necessity. It's powerful, easy to use, and is priced reasonably.

It's when you go outside the standard usage of ACF that things get a bit... thorny...

Into the weeds

Normally, you'd create a field group. And you'd assign that field group based on a set of criteria (a particular page template, or a custom post type, etc.).

Then you create a new post, and you're presented with your custom fields. You enter your data, and it's attached to the post when you save. And accessing that data is trivial from templates.

But what happens if you create a post with custom fields, but you're doing it from inside a PHP function, using wp_insert_post?

What if you want to add content to a field in that post immediately afterward? Surprise! Your standard update_field or add_row code will silently fail. According to the documentation, you need to reference the field name using the field key in that situation.

The field’s key should be used when saving a new value to a post (when no value exists). This helps ACF create the correct ‘reference’ between the value and the field’s settings.

Let's unpack that

ACF stores all of it's custom field values inside standard WordPress meta fields inside the post. In fact, you could just as easily use get_post_meta to retrieve ACF field values under many circumstances. Or even write it back.

But ACF is much more than just a key/value pair, of course. Each field has a whole host of information associated with it. Label name, conditional display logic, field type, etc.

In the post's meta data, ACF creates two different values: the field name, and a field key reference.

Let's say I have a custom, basic Text-type field called "Title".

Inside the post, there will be a title meta data field; this holds the actual value of the field. And then there's a _title field. The underscore means it's a field key reference. The value of that looks something like field_123456. Each field group entry gets it's own unique field key name that looks like that.

Internally, when you call get_field('title') ACF looks up the post meta with an underscore -- _title -- and uses that to pull up the details in the custom field group entry.

If you call get_field('field_123456'), in fact, it will work as well. ACF will reference the field group info, and return the appropriate post meta that way.

Both are valid ways to work with ACF field content.

A brand new post, inserted with wp_insert_post is completely blank. It has no post meta data, outside of the usual timestamp and creation info.

So if you try to run update_field('title', 'My Title', 9999), it does nothing. As if it doesn't exist. Because as far as ACF is concerned, it doesn't.

Not yet.

There's no _title for it to reference for guidance.

But if you update_field('field_123456', 'My Title', 9999), it WILL work. ACF knows right where to go to get it's field details, and it works as normal.

Now here's where it gets tricky

That's all well and good for a simple Text field type.

But what if I have something more complicated? What if I have a Group type, with a Repeater inside that?

Let's say I have a Group called "Vehicles" and a Repeater inside that called "Trucks". (And presumably a "Cars", "Motorcycles", etc, but let's keep it simple!)

And each row inside "Trucks" has a "Model" Text field, and a "Mileage" Number field.

Under normal circumstances I could do:

add_row('vehicles_trucks', ['model'=>'Bigfoot', 'mileage' => '50000'], 9999). _(Note the special, barely documented vehicles_trucks underscore selector notation for these nested fields. )_

But if I've just inserted the post, none of those key field references exist!

The vehicles_trucks selector doesn't work. But the previous fix, using the raw key field reference... say, `add_row('field_902100'...``, well, that doesn't work either! Because which field key reference makes sense in this situation? The one for Vehicles? The one for Trucks?

If you use the key_ field key for Vehicle, it fails. Vehicle is a Group type. Nothing happens.

If you use the key_ field key for Trucks, however, something weird happens. Instead of creating a _vehicles_trucks key reference, it creates a _trucks reference...

At this point it's important to note that ACF is smart and slick... right up until the point it is not.

From what I've discovered, there is no shortcut to adding a new row to a Repeater field nested inside a Group if you've created the post inside PHP, before someone had a chance to hit 'Save' on it from the admin.

If you try to get clever, you might fairly think that underscore notation might apply here. Maybe stick the two together, like field_111111_ field_22222.

But you'd be wrong

No, we have to manually create all of ACF's key references ourselves before we can do anything:

update_post_meta(9999, '_vehicle', 'field_111111');
update_post_meta(9999, '_vehicle_truck', 'field_222222');

THEN we can add_row('vehicles_trucks', ... and insert our data programmatically as expected.

This holds true for even deeper nested content, but at that point maybe you want to rethink what you're doing. 😉

I was surprised by just how little information about this specific use case exists. Hopefully this helps somebody out there!

Hit me up on Twitter if I've made an error anywhere in here.

Elsewhere...