For as nice and as powerful as ACF is, it’s not without it’s shortcomings (but I guess that could be said about any software). For the purposes of this post, though, this is the software about which I’m discussing.

Specifically, I’m talking about the following scenario:

  1. You have a repeater field that’s defined programmatically and thus doesn’t show up in the WordPress administration area,
  2. The data contained within the repeater field is so much that it exceeds the amount of data that can be efficiently stored in the postmeta table.

I was going to cover each of these in one post but given that they aren’t really related, it seems to make most sense to separate the content.

In this post, I’m going to cover how to import a programmatically defined ACF group. And in the next post, I’ll talk about solutions for migrating data into its own table.

Import Programmatically Defined ACF Data

One of the biggest strengths in ACF is the ability to create, define, and manage various groups and fields from within the WordPress administration area. Because the plugin is so flexible, though, it also allows us to define our data programmatically. This is great if you want to quickly and easily get information into the system but it’s not so great if you want to export the data for further management.

And straight from the manual:

Note: Field Groups and Fields registered via code will not be visible/editable via the “Edit Field Groups” admin page.

Bummer, right?

Recall, though, that ACF manages all of its exported data in JSON format. If we want to make these groups and their associated fields accessible and manageable from within the WordPress administration area then why not do the following:

  1. Convert the programmatic output into a JSON file,
  2. Import said JSON file into WordPress,
  3. Tweak as necessary.

To do this, I’m assuming you’re familiar with some of the ACF API functions (and if you’re not, no big deal as it’s easy enough to read through the source code to following along).

Convert Programmatic Data to JSON

Whenever we’re working with defining a group with various fields – in this case, a repeater – then your codebase will likely have a call to the acf_add_local_field_group function.

From the manual, an example of this code may look like this:

acf_add_local_field_group(array(
    'key' => 'group_1',
    'title' => 'My Group',
    'fields' => array (
        array (
            'key' => 'field_1',
            'label' => 'Sub Title',
            'name' => 'sub_title',
            'type' => 'text',
            'prefix' => '',
            'instructions' => '',
            'required' => 0,
            'conditional_logic' => 0,
            'wrapper' => array (
                'width' => '',
                'class' => '',
                'id' => '',
            ),
            'default_value' => '',
            'placeholder' => '',
            'prepend' => '',
            'append' => '',
            'maxlength' => '',
            'readonly' => 0,
            'disabled' => 0,
        )
    ),
    'location' => array (
        array (
            array (
                'param' => 'post_type',
                'operator' => '==',
                'value' => 'post',
            ),
        ),
    ),
    'menu_order' => 0,
    'position' => 'normal',
    'style' => 'default',
    'label_placement' => 'top',
    'instruction_placement' => 'label',
    'hide_on_screen' => '',
));

Notice that the primary argument to this function is an array and since ACF leverages JSON, let’s convert the array to JSON. Specifically, you can write a set of small function of PHP that does something like this:

function arrayToJson(array $array) : string
{
    $json = json_encode($array);
    if ($json === false) {
        throw new \RuntimeException('Unable to encode array to JSON');
    }

    return $json;
}

function writeJsonToDisk(string $json)
{
    $file = fopen('acf.json', 'w');
    fwrite($file, $json);
    return fclose($file);
}

The first function accepts an array and returns a JSON encoded version of the array. Then to write it to disk, pass the string into the writeJsonToDisk function.

To take advantage of this, you’d do the following:

writeJsonToDisk(
    arrayToJson($acfArray)
);

Then acf.json would write to wherever you’ve run the script.

Import the JSON File

At this point, you can import the JSON file that you just exported into ACF so that it can be managed via WordPress.

This should appear exactly as the array has defined it. If it doesn’t then you may have a few things to tweak but it should be able to all be done from within the administration area.

Finally, remember to remove the old code or add a simple return statement above the acf_add_local_field_group function call to make sure it’s no longer run.

Better yet, maybe add a return statement first until you know everything is working as you like it, then remove it. That one extra step can save you a bit of time should something go sideways.

Tweak as Necessary

Though you may not need to make any changes to the structure you’ve imported, I needed to do so. For example, maybe you need a little more flexibility.

Case in point, I needed to add a conditional that allowed me to not only use it on a page with a template but also work on the general page post type. Because of the steps laid out above, I was able to easily make that change.

All I needed to do was add the one more option that didn’t lock me into a page with a template. Technically, I could have replaced it but I needed to maintain both options namely because of the use of custom database tables.

Custom Database Tables?

The problem with certain types of groups and fields in ACF is that they don’t scale very well. That is, you can quickly run out of space in the postmeta table if you’re using, say, a repeater.

This means that if you’re trying to keep track of a list of nested data then you’re eventually going to hit a maximum limit and be unable to save anymore data. But that’s because of a limitation of the core WordPress database and trying to shove so much information into a single column.

There are better solutions available for this such as using a separate database table. And next, I’ll share with you the what and why I use what I use.