Field groups#

A field group is the root of an ACF Chef definition.

Create one with FieldGroup::make() and give it a name:

use AcfChef\FieldGroup;

$group = FieldGroup::make('hero');

make() returns an instance of the class it was called on. This matters when you define a FieldGroup subclass: HeroGroup::make() returns a HeroGroup, not the base class.

Names and titles#

The constructor takes one argument: the group name.

ACF Chef derives the initial title from that name by replacing underscores and hyphens with spaces, then applying title case:

$group = FieldGroup::make('hero_banner');

$group->toArray()['title'];
// Hero Banner

Override the derived title through group-level configuration:

$group->config([
    'title' => 'Homepage Hero',
]);

A string value becomes the emitted title. If title is present but is not a string, ACF Chef ignores it and derives the title from the group name again.

Configure the group#

Group-level config() accepts an array of ACF field-group settings:

$group = FieldGroup::make('hero_banner');

$group->config([
    'title' => 'Hero Banner',
    'style' => 'seamless',
    'position' => 'acf_after_title',
    'menu_order' => 3,
    'hide_on_screen' => ['the_content'],
]);

There is no closure form of group-level config().

Deferred field configuration receives a real extended-acf field object at compile time. A field group has no corresponding extended-acf object, so there is nothing for a group configuration closure to receive.

The example compiles to this shape before any fields or locations are added:

[
    'title' => 'Hero Banner',
    'key' => 'group_hero_banner',
    'fields' => [],
    'location' => [],
    'style' => 'seamless',
    'position' => 'acf_after_title',
    'menu_order' => 3,
    'hide_on_screen' => ['the_content'],
]

The first four keys always appear in this order:

  1. title
  2. key
  3. fields
  4. location

Any remaining group settings follow in the order they were configured.

location is always present. A group with no location rules emits an empty array rather than omitting the key.

Special group settings#

Four configuration keys are interpreted separately when the group is converted to an array.

title#

A string overrides the title derived from the group name.

$group->config([
    'title' => 'Primary Hero',
]);

A non-string value is not emitted. ACF Chef falls back to the derived title instead.

key#

An explicit group key overrides the key produced by the active key strategy:

$group->config([
    'key' => 'group_legacy_hero',
]);

The value is emitted exactly as supplied.

Use an explicit key when the group must keep an identifier already used by an existing site or another registration tool.

location#

You may supply a complete ACF location array through config():

$group->config([
    'location' => [
        [
            [
                'param' => 'post_type',
                'operator' => '==',
                'value' => 'page',
            ],
        ],
    ],
]);

That array is used only when you have not called location() or andLocation() on the group.

As soon as you add a fluent location rule, the fluent rules own the emitted location value. A location array in config() cannot silently replace them.

See Locations and registration for the fluent location API.

fields#

A fields value in group configuration is discarded:

$group->config([
    'fields' => [
        // Ignored.
    ],
]);

Fields come from the ACF Chef node tree. Accepting a second field source here would allow the configured array and the declared tree to disagree.

The title, key, location and fields entries are removed from the remaining settings before they are appended to the output, so none of them can appear twice.

Group configuration has no shorthands#

Configuration shorthands belong to field cursors. Whatever your application registers with Chef::shorthand() expands on a field:

Chef::shorthand('half', fn (): array => ['wrapper' => ['width' => 50]]);

$group->text('title')->config([
    'half' => true,
]);

The same key at group level is not expanded:

$group->config([
    'half' => true,
]);

That produces a literal top-level half key in the field-group array, which ACF does not define as a field-group setting.

Group settings are ACF’s own: style, position, menu_order, hide_on_screen and the rest.

Override the key strategy for one group#

Set a key strategy directly on a field group when only that definition should use it:

use AcfChef\FieldGroup;
use AcfChef\KeyStrategy\HashedKeys;

$group = FieldGroup::make('hero');
$group->keyStrategy(new HashedKeys());

The override governs the group key and every field and layout key compiled underneath it.

Other groups continue to use the global strategy configured through Chef::keyStrategy().

See Field keys for the built-in strategies, custom strategies and compatibility rules.

Subclass a field group#

A field group can own its definition in a class:

use AcfChef\FieldGroup;

final class HeroGroup extends FieldGroup
{
    public function __construct(string $name)
    {
        parent::__construct($name);

        $this
            ->text('title')
            ->textarea('summary')
            ->location('post_type', 'page');
    }
}

Create it through the subclass:

$hero = HeroGroup::make('hero');

$array = $hero->toArray();

The constructor must retain the base signature:

public function __construct(string $name)

FieldGroup uses a consistent constructor, and make() passes exactly the name. Changing the required parameters makes the inherited factory unusable.

Call parent::__construct($name) before declaring fields. The parent constructor establishes the group name, derived title and field tree that the public builder methods use.

All FieldGroup state is private. A subclass builds and configures itself through the same public methods as any other definition; it does not reach into the node tree or settings directly.

Chef::group() always creates a plain FieldGroup. It does not infer or instantiate your subclass. Use HeroGroup::make() or construct the subclass yourself when the definition belongs to a custom class.

Typehint the contract#

Code that accepts a field group without requiring the concrete implementation should typehint:

AcfChef\Contracts\FieldGroup

The contract exposes the supported field-group surface while allowing the caller to receive the base class or one of your subclasses.