stoutlogic/acf-builder
Fluent builder for Advanced Custom Fields Pro (ACF) field groups. Define fields and locations in PHP, reuse configs, and generate ACF’s registration arrays with less boilerplate. Ideal for keeping ACF setup in version control.
Installation
composer require stoutlogic/acf-builder
Publish the config file:
php artisan vendor:publish --provider="StoutLogic\AcfBuilder\AcfBuilderServiceProvider" --tag="acf-builder-config"
Basic Setup
FieldGroup class (e.g., app/Acf/FieldGroups/PageHero.php):
use StoutLogic\AcfBuilder\FieldsBuilder;
class PageHero extends FieldsBuilder
{
public function getTitle()
{
return 'Page Hero Settings';
}
public function setup()
{
$this->setLocation('post_type', '==', 'page');
$this->setLocation('post_status', '==', 'publish');
$this->addField([
'label' => 'Hero Image',
'instructions' => 'Upload a hero image for the page.',
'name' => 'hero_image',
'type' => 'image',
'required' => 0,
]);
}
}
config/acf-builder.php:
'field_groups' => [
\App\Acf\FieldGroups\PageHero::class,
],
Sync Fields Run the Artisan command to push fields to ACF:
php artisan acf:builder:push
Access Fields in Templates
Use get_field() or get_sub_field() in Blade:
$heroImage = get_field('hero_image');
<img src="{{ $heroImage['url'] }}" alt="{{ $heroImage['alt'] }}">
Dynamic Field Groups
setup() to dynamically adjust fields:
public function setup()
{
$this->addField([
'name' => 'featured_product',
'type' => 'post_object',
'post_type' => ['product'],
]);
if ($this->getOption('show_variations')) {
$this->addField([
'name' => 'product_variations',
'type' => 'repeater',
'sub_fields' => [
['name' => 'variation_title', 'type' => 'text'],
],
]);
}
}
Reusable Field Logic
protected function addImageField($name, $label)
{
$this->addField([
'name' => $name,
'label' => $label,
'type' => 'image',
'return_format' => 'array',
]);
}
Integration with Eloquent
hasMany or morphTo:
// In a model (e.g., Post.php)
public function acfFields()
{
return $this->morphMany(\App\Models\AcfField::class, 'fieldable');
}
acf() helper to fetch fields:
$post->acf()->where('name', 'hero_image')->first();
Localization Support
$this->addField([
'name' => 'title',
'type' => 'text',
'label' => __('Title', 'lang'),
'instructions' => __('Enter the title in your language.', 'lang'),
]);
Field Group Inheritance
class BaseContent extends FieldsBuilder
{
public function setup()
{
$this->addField(['name' => 'content', 'type' => 'wysiwyg']);
}
}
class BlogPost extends BaseContent
{
public function setup()
{
parent::setup();
$this->addField(['name' => 'excerpt', 'type' => 'textarea']);
}
}
}
Modifying Nested Fields (New in v1.12.0)
getField() and getLayout() to modify or remove nested fields in Flexible Content or Repeater fields:
public function setup()
{
$this->addField([
'name' => 'sections',
'type' => 'flexible_content',
'layouts' => [
'hero' => [
'title' => 'Hero Section',
'sub_fields' => [
['name' => 'sub_title', 'type' => 'text'],
],
],
],
]);
// Modify nested field after setup
$this->getLayout('hero')->getField('sub_title')->setLabel('Updated Subtitle');
// Remove nested field using shorthand syntax
$this->removeField('sections->hero->sub_title');
}
Field Name Conflicts
group_name_field) to avoid collisions.php artisan acf:builder:dump to list all registered fields.Location Rules Overlap
setLocation() rules may cause unexpected field visibility. Test with:
php artisan acf:builder:validate
Caching Issues
php artisan acf:builder:clear-cache
config/acf-builder.php:
'cache' => false,
Field Type Limitations
google_map) require additional setup (e.g., API keys). Check the ACF docs for prerequisites.Database Bloat
limit in setup() to restrict entries:
$this->addField([
'name' => 'gallery',
'type' => 'repeater',
'limit' => 10,
]);
Nested Field Modifications (v1.12.0)
-> delimiter is used for nested field paths (e.g., sections->hero->sub_title). Avoid using . in field names to prevent unintended conflicts.. are unaffected. Future major versions may revisit the delimiter choice.Log Field Group Errors
Enable logging in config/acf-builder.php:
'log' => [
'enabled' => true,
'path' => storage_path('logs/acf-builder.log'),
],
Validate Field Groups Run the validator to catch syntax errors:
php artisan acf:builder:validate
Inspect ACF JSON Dump the raw ACF JSON for a field group:
php artisan acf:builder:dump --group="PageHero"
Nested Field Debugging
dd($builder->getLayout('layout_name')->getFields()) to inspect nested fields before modification.Custom Field Types
StoutLogic\AcfBuilder\FieldsBuilder to add custom field logic:
class CustomFieldBuilder extends FieldsBuilder
{
public function addCustomField($config)
{
$this->fields[] = array_merge($config, [
'type' => 'custom_type',
]);
}
}
Pre/Post Push Hooks
// In AppServiceProvider
public function boot()
{
AcfBuilder::extend(function ($builder) {
$builder->addField(['name' => 'custom_field', 'type' => 'text']);
});
}
Dynamic Field Group Registration
$fieldGroups = \App\Models\FieldGroup::all();
foreach ($fieldGroups as $group) {
$class = "App\Acf\FieldGroups\\" . $group->name;
if (class_exists($class)) {
AcfBuilder::register($class);
}
}
Flexible Content Layout Modifications (New in v1.12.0)
How can I help you explore Laravel packages today?