Locations and registration#
A field group needs location rules to tell ACF where it should appear.
ACF Chef also gives you three ways to get a completed definition out of the builder:
toArray()for the final ACF array.register()to register that array directly with ACF.fields()for interoperability with plain extended-acf code.
Chef::group() is the convenience path that builds a group. When it reaches ACF is your call.
Location rules#
Start a location group with location():
$group->location('post_type', 'page');
With two arguments, the second argument is the value and the operator defaults to ==.
The equivalent three-argument form is:
$group->location('post_type', '==', 'page');
The method behaves according to argument count rather than whether the third argument happens to be null.
AND and OR groups#
Each call to location() starts a new rule group.
ACF ORs those groups against one another:
$group
->location('post_type', 'page')
->location('post_type', 'post');
This means:
post_type == page
OR
post_type == post
Use andLocation() to add another rule to the most recently opened group:
$group
->location('post_type', 'page')
->andLocation('page_type', '!=', 'front_page')
->location('post_type', 'post');
The emitted location structure is:
[
[
[
'param' => 'post_type',
'operator' => '==',
'value' => 'page',
],
[
'param' => 'page_type',
'operator' => '!=',
'value' => 'front_page',
],
],
[
[
'param' => 'post_type',
'operator' => '==',
'value' => 'post',
],
],
]
The outer list is ORed. Rules within each inner list are ANDed.
If no location group is open yet, andLocation() behaves like location() and starts one.
Location is always present#
A compiled field group always contains a location key.
With no location rules:
[
'location' => [],
]
The key is never omitted.
You can also supply a raw location array through group-level config(). Fluent location() calls take precedence once you use them. See Field groups for that interaction.
A location can end a field chain#
You do not need to break a fluent field chain before adding the group location:
$group
->text('title')
->trueFalse('featured')
->location('post_type', 'page');
The cursor returned for featured does not declare location(), so the call delegates to its field set. The field set delegates again to the field group, which does declare the method.
The location is therefore added to the group, not to the field.
location() returns the field group.
See Nesting and chaining for cursor delegation in full.
AcfChef\Location#
AcfChef\Location extends Extended\ACF\Location.
It subclasses the upstream type rather than wrapping it so the same location objects can be passed to register_extended_field_group(), which typehints the extended-acf class.
It also corrects a subclassing problem in the upstream where() factory. The upstream method declares a static return type but constructs self, so invoking it on a subclass can produce a TypeError.
ACF Chef’s implementation constructs static instead.
Its constructor is final, so subclasses can add behaviour without changing the construction contract that where() relies on.
toArray()#
Call toArray() when you need the final ACF-ready definition:
$array = $group->toArray();
Compilation has already finished:
- Field objects have been serialised.
- The active key strategy has been applied.
- Conditional references have been resolved.
collapsedreferences have been rewritten.
The top-level keys have a stable order:
[
'title' => ...,
'key' => ...,
'fields' => ...,
'location' => ...,
// Remaining group settings follow.
]
Additional group settings appear afterwards in the order they were configured.
Use this form when you are handing the definition to another API yourself or asserting on the final ACF structure in a test.
register()#
register() compiles the field group and passes the resulting array to acf_add_local_field_group():
$registered = $group->register();
It returns the same array it registered.
That makes the method useful when a registration callback also wants to inspect, log or assert on exactly what it handed to ACF.
Nothing calls it for you. ACF Chef adds no hooks, so a group reaches ACF only where your application decides it should — normally an acf/init callback:
add_action('acf/init', function () use ($group) {
$group->register();
});
acf/init is where ACF becomes ready to accept local field groups. Registering earlier is what ACF itself warns about.
register() assumes WordPress and ACF are available.
It does not guard acf_add_local_field_group(). Calling it outside WordPress therefore fails rather than silently pretending registration succeeded.
Use toArray() when you only need the compiled value outside WordPress.
Chef::group()#
Chef::group() is the convenience form:
use AcfChef\Chef;
$hero = Chef::group('hero', function ($group) {
$group
->text('title')
->location('post_type', 'page');
});
It builds the group and returns it. Nothing is compiled, hooked or registered.
The group therefore stays mutable for as long as you hold it, right up to the register() call:
$hero = Chef::group('hero', function ($group) {
$group
->text('title')
->location('post_type', 'page');
});
$hero->modify('title', [
'required' => true,
]);
That edit is part of the definition that register() will compile. Once compiled, the array ACF holds is a copy: editing the group afterwards changes nothing that has already been registered.
See Getting started for where to put the declaration and the registration.
fields() for extended-acf interop#
fields() returns the real extended-acf field objects represented by the definition:
$fields = $group->fields();
That lets an ACF Chef definition feed code written for plain extended-acf:
use AcfChef\Location;
register_extended_field_group([
'title' => 'Hero',
'fields' => $group->fields(),
'location' => [
Location::where('post_type', 'page'),
],
]);
The handover is useful, but it is not lossless.
fields() does not preserve the key strategy#
The returned objects have not been serialised yet, so they do not have their final keys.
Whatever consumes them later calls extended-acf’s own toArray() method. At that point extended-acf generates its normal hashed keys rather than applying the ACF Chef key strategy.
For the same field:
$group->fields()[0]->toArray('hero');
// key: field_46a2d294
while:
$group->toArray()['fields'][0];
// key: field_hero_title
The group’s key strategy does not survive the fields() handover.
Use toArray() whenever stable ACF Chef keys matter. On an existing site, they normally do.
Conditional rules also fall back to extended-acf#
Conditional rules on objects returned by fields() are handed over before ACF Chef performs its normal final serialisation and key rewrite.
Their targets are still checked against the ACF Chef node tree first. An unresolvable target therefore fails instead of becoming a silent dead rule.
Once the objects leave ACF Chef, however, the eventual conditional serialisation uses extended-acf’s own name-based resolution rather than ACF Chef’s final key mapping.
Use toArray() when you need ACF Chef’s complete conditional-resolution behaviour as well as its key strategy.
fields() is an interoperability escape hatch. toArray() is the canonical compiled output.