Coder Social home page Coder Social logo

acf-builder's Introduction

ACF Builder

Create configuration arrays for Advanced Custom Fields Pro using the builder pattern and a fluent API.

Quickly create, register, and reuse ACF configurations, and keep them in your source code repository. To read more about registering ACF fields via php consult https://www.advancedcustomfields.com/resources/register-fields-via-php/

Latest Stable Version Build Status Scrutinizer Code Quality Join the chat at https://gitter.im/StoutLogic/acf-builder

Simple Example

$banner = new StoutLogic\AcfBuilder\FieldsBuilder('banner');
$banner
    ->addText('title')
    ->addWysiwyg('content')
    ->addImage('background_image')
    ->setLocation('post_type', '==', 'page')
        ->or('post_type', '==', 'post');

add_action('acf/init', function() use ($banner) {
   acf_add_local_field_group($banner->build());
});

$banner->build(); will return:

[
    'key' => 'group_banner',
    'title' => 'Banner',
    'fields' => [
        [
            'key' => 'field_title',
            'name' => 'title',
            'label' => 'Title',
            'type' => 'text'
        ],
        [
            'key' => 'field_content',
            'name' => 'content',
            'label' => 'Content',
            'type' => 'wysiwyg'
        ],
        [
            'key' => 'field_background_image',
            'name' => 'background_image',
            'label' => 'Background Image',
            'type' => 'image'
        ],
    ],
    'location' => [
        [
            [
                'param' => 'post_type',
                'operator' => '==',
                'value' => 'page'
            ]
        ],
        [
            [
                'param' => 'post_type',
                'operator' => '==',
                'value' => 'post'
            ]
        ]
    ]
]

As you can see it saves you a lot of typing and is less error-prone. But brevity and correctness isn't the only benefit, you can reuse field configurations in multiple places. For example, a group of fields used for backgrounds:

Reuse Example

use StoutLogic\AcfBuilder\FieldsBuilder;

$background = new FieldsBuilder('background');
$background
    ->addTab('Background')
    ->addImage('background_image')
    ->addTrueFalse('fixed')
        ->instructions("Check to add a parallax effect where the background image doesn't move when scrolling")
    ->addColorPicker('background_color');

$banner = new FieldsBuilder('banner');
$banner
    ->addTab('Content')
    ->addText('title')
    ->addWysiwyg('content')
    ->addFields($background)
    ->setLocation('post_type', '==', 'page');

$section = new FieldsBuilder('section');
$section
    ->addTab('Content')
    ->addText('section_title')
    ->addRepeater('columns', ['min' => 1, 'layout' => 'block'])
        ->addTab('Content')
        ->addText('title')
        ->addWysiwyg('content')
        ->addFields($background)
        ->endRepeater()
    ->addFields($background)
    ->setLocation('post_type', '==', 'page');

Here a background field group is created, and then used in two other field groups, including twice in the section field group. This can really DRY up your code and keep your admin UI consistent. If you wanted to add a light/dark field for the text color field based on the background used, it would just need to be added in one spot and used everywhere.

Install

Use composer to install:

composer require stoutlogic/acf-builder

If your project isn't using composer, you can require the autoload.php file.

Tests

To run the tests you can manually run

vendor/bin/phpunit

or you can use the built in gulp task to run it on file change

npm install
gulp

Requirements

PHP 5.4 through 7.2 supported but automated tests cannot be run anymore so it might stop working at some point.

= 7.4, 8 Tested

Documentation

See the wiki for more thorough documentation. The documentation has its own repository and accepts pull requests for contributions. Any merges to master will get synced with the wiki here under the main project.

acf-builder's People

Contributors

androlax2 avatar aprokopenko avatar dalkmania avatar davddo avatar gitter-badger avatar guix77 avatar ken-gladeye avatar log1x avatar michaelw85 avatar nlemoine avatar scrutinizer-auto-fixer avatar stevep avatar themosaad avatar titouanmathis avatar tombroucke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acf-builder's Issues

Massive DB conflicts

Hi there,

just got started using the acf-builder and very much appreciate the library!

I ran into an issue and I suspect a bug of some sorts with acf-builder to be responsible:

I register a number of different groups / fields and they somehow overlap in the DB. Different entries are merged into one although the field names are unique and I cannot wrap my head around it. Everything seems to be set up fine...?

Example

The value I enter in the backend for acf[field_basic_cta_title] (save / refresh post) is being overwritten by the entry in acf[field_home_about_title]!

Meaning I cannot set the about title at all and it just keeps ignoring what I enter into the field...

Steps I took

  • Renamed the field groups
  • Renamed the fields so the names wouldn't match any more (f.ex. addText('about_title') – this is obviously not needed though since the fields are unique due to their group-labels...)
  • Deleted the post & created a new post
  • Tried registering the fields without the builder-array, e.g. each field gets its own acf_add_local_field_group since that could interfere somehow

Nothing changes, still seeing the errors...

Code

Field group A

/**
 * BASIC -- CTA / Call to action
 */
$basic_cta = new FieldsBuilder('basic_cta');
$basic_cta
  ->addText('title')
  ->addText('subtitle')
  ->addText('button_label')
  ->addUrl('button_target')
  ->addRadio('style')
    ->addChoices('light', 'dark', 'white')
    ->setDefaultValue('light')
  ->addRadio('positon')
    ->addChoices(['float' => 'Nebeneinander'], ['break' => 'Untereinander'])
    ->setDefaultValue('float')
  ->setLocation('post_type', '==', 'page');

Field group B

/**
 * HOME -- About-Section
 */
$home_about = new FieldsBuilder('home_about');
$home_about
  ->addImage('image')
  ->addText('title')
  ->addWysiwyg('text')
    ->setConfig('delay', 1)
  ->setGroupConfig('order_no', 4)
  ->setLocation('page_type', '==', 'front_page');

Builder

/**
 * BUILD -- Now we create / register the fields
 * The array should really build automatically, room for improvement here...
 */

$acf_fields = [
  $basic_cta,
  [...]
  $home_about,
  [...]
];

add_action('acf/init', function() use ($acf_fields) {
  foreach ($acf_fields as $acf_field) {
    if ($acf_field instanceof FieldsBuilder) {
      acf_add_local_field_group($acf_field->build());
    }
  }
});

What I can try to further debug this? Any idea what might be causing these problems?

Any help is greatly appreciated!

Thanks & regards,
Henning

P.S.: I use ACF Fluent to retrieve / display the fields but to rule that stuff out I tested with direct calls via the_field('field_home_about_title') but to no avail...

Add option to disable conditional field check.

I have a few ACF front end forms that draw from multiple field groups.

I'd like to be able to do conditional logic between the different field groups. However, I get stuck with an error that ACF Builder throws when the field does not exist in the group.

It would be really awesome if there could be an additional parameter on conditional() that would ignore this check.

If I alter the array to add my conditionals they work as expected across field groups.

Thanks!

Radio field "defaultValue" method is not found.

I'm trying to add a radio field and running into an issue with the defaultValue method.

->addRadio('project_thumbnail_extend', ['label' => 'Extend Thumbnail'])
   ->addChoice('none', 'None')
   ->addChoice('left', 'Left')
   ->addChoice('right', 'Right')
   ->defaultValue('none')

( ! ) Fatal error: Uncaught Exception: No such function: defaultValue in /Users/christianmagill/Code/Sites/influencedesign/web/app/themes/influencedesign/vendor/stoutlogic/acf-builder/src/ParentDelegationBuilder.php on line 69

Recursive Conditional

If I were to do something like this:

$builder = new FieldsBuilder(
	'content_areas',
	array(
		'button_label' => __( 'Add Content', 'my-domain' ),
	)
)
	->addFlexibleContent( 'flexible_content' )
	->addLayout( 'hero' )
		->addImage( 'hero_image' )
		->addWysiwyg( 'hero_text' )
		->addRepeater( 'cta', array(
			'label' => __( 'Add Call to Action', YMCORE_PLUGIN_DOMAIN ),
		) )
			->addText( 'cta_text', array(
				'label' => __( 'Text', YMCORE_PLUGIN_DOMAIN ),
			) )
			->addSelect( 'link_type' )
			->addChoices( 'internal', 'external', 'text' )

			->addPageLink( 'cta_link', array(
				'post_type' => array(
					'page',
					'post',
					'product',
				),
			) )
				->conditional('link_type', '==', 'internal')
			->addSelect( 'size' )
			->addChoices( 1, 2, 3, 4 )
			->addSelect( 'color' )
			->addChoices( 'red', 'white', 'blue' )
			->endRepeater();

The conditional outputs expects link_type to be field of key field_cta_link_type but in this case the field key is field_content_areas_flexible_content_hero_cta_link_type due to proper recursion. The builder currently errors out. Would you please provide support for the conditional method within the Repeater and FlexibleContent methods?

Currently there is a workaround, but I'd like to stay consistent.

New conditionals with only two parameters.

@stevep,

ACF 5.7 brings with it new field conditionals that only have two parameters.

Unfortunately, ACF Builder doesn't allow conditions with two parameters.

I've tried passing null but while it successfully builds, it stops the validation from working properly in certain scenarios.

Unfortunately this is causing a breaking issue for me.

The two conditionals in question are "==empty" and "!=empty".

Here's an example of how the fields output through the generate PHP option in ACF.

'conditional_logic' => array(
				array(
					array(
						'field' => 'field_5b1305029be7d',
						'operator' => '>',
						'value' => '6.4999999',
					),
					array(
						'field' => 'field_5b1305029be7d',
						'operator' => '<',
						'value' => '8.5000000',
					),
					array(
						'field' => 'field_5b1305029be7d',
						'operator' => '!=empty',
					),
				),
			),

Activate method?

Thanks for this awesome library!

I'm pretty sure I'll be using it on all my ACF projects moving forward.

Reading through the documentation, I wonder why you don't simply include an activate method for your FieldsBuilder that sets up the necessary hook?

Mutability / Updating fields when using addFields

Hi there,

I am having trouble updating fields when using the addFields-functionality – my fields disappear when editing the original FieldsBuilder.

Following the wiki entry on composing fields I assumed that I could modify the original fields but that wouldn't have any effect on the master field(s)...

When adding a FieldsBuilder to another FieldsBuilder with addFields or addLayout, that FieldsBuilder is cloned, which will lock in the fields and configs. So updating those fields at a later time on the original FieldsBuilder will not have an effect on the cloned versions. These cloned copies embedded in other FieldsBuilder can still be updated separately, so they are still mutable.

...but wouldn't destroy / hide them?

This is my setup (exemplary):

/**
 * HOME -- Slider
 */
$home_slider = new FieldsBuilder('home_slider');
$home_slider
  ->addRepeater('slider', ['min' => 1, 'max' => 5, 'layout' => 'table'])
    ->addImage('image')
    ->addText('title', [
      'wp-typography' => 'content'
    ])
    ->addWysiwyg('text', [
      'wp-typography' => 'content'
    ])
    ->endRepeater()
  ->addText('claim');

/**
 * HOME MASTER -- All home fields tabbed together
 */
$home = new FieldsBuilder('home');
$home
  ->addTab('Slider')
    ->addFields($home_slider)
  ->addTab('CTA Icons')
    ->addFields($home_cta_icons)
  ->setLocation('page_type', '==', 'front_page');
  • Does it make sense that the whole home-section / -fields disappears when I modify any of included fields (f.ex. removing and/or adding a field to home_slider after initially registering the home-field)?
  • How would I go about correctly to update the master field? Any way of forcing an update on the field logic? I tried removing it from acf_add_local_field_group and re-initiating but to no avail...

Sorry but either I haven't grasped the concept well enough (high probability) or this is a bug maybe?

Thanks for your insights.

Regards,
Henning

Php error on build

Got a php error for the $banner->build() line.

Xdebug: Fatal error: Uncaught Error: Call to a member function build() on null

Removing the function() wrapper solves the issue and the fields display properly. I used this line:

add_action('acf/init', acf_add_local_field_group($banner->build()) );

but i get 2 php notices for that new line:

Undefined offset: 0

Wonderful work btw, love the composer use, will try and get more into it in the following weeks. 👍

Breaking chaining?

Is there any way to break the chaining and pick it back up?

I'd like to dynamically set the "setLocation" and necessary "or" methods.

For example:

function createFields(){

        $archiveFields = new FieldsBuilder( 'taxonomy_archive_group', [

        ]);
        $archiveFields
            ->addText('taxonomy_archive_title', [
                'label' => 'Title',
                'instruction' => 'Replace the default archive title',
            ])
            ->addWysiwyg( "taxonomy_archive_content", [
                'label' => 'Content',
                'toolbar'      => 'full',
                'media_upload' => true,
                'instruction' => 'Add optional content to the archive page.',
            ] )
            ->setLocation( 'taxonomy', '==', "all" );

        $count = 0;

        foreach($this->taxonomies as $taxonomy){
            if($count){
                $archiveFields->setLocation('taxonomy' == $taxonomy);
            }
            else{
                $archiveFields->or('taxonomy' == $taxonomy);
            }
            $count++;
        }

        acf_add_local_field_group( $archiveFields->build() );

    }

Any ideas?

Using addRepeater and Conditional together

Hi we want to add conditional logic to a repeater, but how? Here the code of our try;

`
$field_group_title = ‘Diensten inhoudsopgave’;
$footer_settings = new FieldsBuilder( sanitize_title_with_dashes( $field_group_title ) );

    $footer_settings
        ->setGroupConfig( ‘title’, $field_group_title )
        ->addRadio( ‘toc_type’, [
            ‘label’   => ‘Type inhoudsopgave’,
            ‘layout’  => ‘vertical’,
            ‘return_format’ => ‘array’
        ] )
        ->addChoices([
            ‘page_scroll’ => ‘Links binnen de pagina’,
            ‘page_links’  => ‘Links naar andere pagina’s’,
        ] )
        ->addRepeater(‘page_links’)
            ->addText(‘title’)
            ->addPostObject(‘page_id’, [
                ‘label’=> ‘Pagina’,
                ‘post_type’ => ‘page’,
                ‘return_format’ => ‘id’,

            ])
        ->endRepeater()
        ->addText(‘title’)
        ->conditional( ‘toc_type’, ‘==‘, ‘page_links’)
        ->setLocation( ‘page_template’, ‘==‘, ‘template-services.php’ );

`

Composing Fields and using them throws error

How you do register your Reuse Example?
I'm trying to:

add_action('acf/init', function() use ($section) {
  acf_add_local_field_group($section->build());
});

but I'm getting a lot of warnings:

( ! ) Warning: Illegal string offset 'parent' in /srv/www/example.com/current/web/app/plugins/advanced-custom-fields-pro/pro/acf-pro.php on line 307
( ! ) Notice: Undefined index: type in /srv/www/example.com/current/web/app/plugins/advanced-custom-fields-pro/pro/acf-pro.php on line 288

Shorthand for adding multiple choices via arrays

I think the documentation should be clearer as for adding multiple choices via [key => value] arrays.

https://github.com/StoutLogic/acf-builder/wiki/choices#shorthand-for-adding-multiple-choices

It's not clear which is the "label" and which is the field "value", and as it turns out the field "value" should be the key and the field "label" should be the value which can be confusing.

I also think it would be great to support adding multiple entries in a single associative array. Currently this causes issues where the values are used as both the keys and the values in the output array.

Flexible content, repeaters and conditionals

I'm having some issues with a mix of flexible content fields, repeaters, and conditionals.

This code throws error

Fatal error: Uncaught StoutLogic\AcfBuilder\FieldNotFoundException: Field 'flexible_content_enabled' not found. in .../stoutlogic/acf-builder/src/FieldManager.php on line 204

$flexibleContent
            ->addTrueFalse( 'flexible_content_enabled', [
                'label' => 'Enable Flexible Content',
                'message' => 'Enable enhanced flexible content for this page.'
            ])
            ->addFlexibleContent( 'flexible_content_sections')
                ->addLayout('text')
                    ->addWysiwyg('content')
                ->addLayout('accordion')
                    ->addRepeater('accordion', [
                        'layout' => 'block',
                    ])
                        ->addText('accordion_title', ['label' => 'Title'])
                        ->addWysiwyg('accordion_content', ['label' => 'Content'])
                    ->endRepeater()
                ->addLayout('panel')
                    ->addImage('panel_image')
                    ->addWysiwyg('panel_content')
            ->conditional('flexible_content_enabled', '==', '1')
            ->setLocation( 'post_type', '==', 'page' );

However, if I move the 'panel' layout before the accordion things work as expected.

checkbox serialized data error in storing data

Pretty sure this is a bug..

So if I set the data like so:

$row = new StoutLogic\AcfBuilder\FieldsBuilder('page_builder', [
    'title' => 'Responsive Page Builder',
    'style' => 'seemless',
    'hide_on_screen' => ['content_editor']
]);

$row
    ->setLocation('post_type', '==', 'page')
        ->or('post_type', '==', 'post')
    ->addRepeater('row', [
        'title' => 'Responsive Columned Row',
        'layout' => 'block'
        ])
        ->addCheckbox('style_options')
            ->addChoice('vert_align_center')
        ->endRepeater();

acf_add_local_field_group($row->build());

The data in the database is stored like so:

meta_id | post_id | meta_key            | meta_value
2008    | 105     | row_0_style_options | a:1:{i:0;s:17:"vert_align_center";}

However, if I store the data like so:

$row = new StoutLogic\AcfBuilder\FieldsBuilder('page_builder', [
    'title' => 'Responsive Page Builder',
    'style' => 'seemless',
    'hide_on_screen' => ['content_editor']
]);

$row
    ->setLocation('post_type', '==', 'page')
        ->or('post_type', '==', 'post')
    ->addRepeater('row', [
        'title' => 'Responsive Columned Row',
        'layout' => 'block'
        ])
        ->addCheckbox('style_options')
            ->addChoice('vert_align_center', ['title' => 'Vertical Align Center'])
        ->endRepeater();

acf_add_local_field_group($row->build());

The data in the database is stored like so:

meta_id | post_id | meta_key            | meta_value
2008    | 105     | row_0_style_options | a:1:{i:0;s:17:"title";}

Realized this when using custom get_post_meta() database pulls.

Add Range Field

ACF v5.6.2 was released today with the addition of a new Range field.

->addRange()? 💓

Thanks a lot in advance. ACF Builder makes me very happy.

Conditional issue with addFields

I updated from an older version to get the recursive conditional fix #26 and now I get an error where ever I used addFields with a conditional.

Uncaught exception 'Exception' with message 'No such function: conditional' in /Users/Removed/vendor/stoutlogic/acf-builder/src/ParentDelegationBuilder.php:69

Tested with a very simple setup:

$fields = new FieldsBuilder('fields');
$fields
  ->addText('fields_text');

$builder = new FieldsBuilder('fields_conditional');
$builder
  ->addSelect('select')
    ->addChoice('hide')
    ->addChoice('show')
  ->addText('text')
  ->addFields($fields)
    ->conditional('select', '==', 'show')
 ->setLocation('post_type', '==', 'page');

By the way, love the builder, quick and easy to use.

Conditionals not working when added via addFields method.

When conditionals are added using the addFields method they fail to work properly.

In the example below, the status of the 'type' field has no bearing on the conditionals. The initial settings of the conditionals are wrong based on the defaults, and changing the 'type' field value has no affect.

$linkFields = new FieldsBuilder('link_fields');
    $linkFields
        ->addRadio('type', [
            'layout' => 'horizontal'
            ])
            ->addChoices([
                'none' => 'None',
                'internal' => 'Internal',
                'external' => 'External',
                'file' => 'File'
            ])
        ->addText('label')
            ->conditional('type', '!=', 'none')
        ->addPageLink('internal')
            ->conditional('type', '==', 'internal')
        ->addUrl('external')
            ->conditional('type', '==', 'external')
        ->addFile('file')
            ->conditional('type', '==', 'file');

    $test = new FieldsBuilder( 'test' );
    $test
        ->addRepeater( 'links', [ 'layout' => 'block' ] )
            ->addFields( $linkFields )
        ->endRepeater()
        ->setLocation( 'post_type', '==', 'page' );

    acf_add_local_field_group($test->build());

Generates

Array
(
    [key] => group_test
    [title] => Test
    [fields] => Array
        (
            [0] => Array
                (
                    [type] => repeater
                    [name] => links
                    [label] => Links
                    [key] => field_test_links
                    [layout] => block
                    [button_label] => Add Link
                    [sub_fields] => Array
                        (
                            [0] => Array
                                (
                                    [choices] => Array
                                        (
                                            [None] => None
                                            [Internal] => Internal
                                            [External] => External
                                            [File] => File
                                        )

                                    [type] => radio
                                    [name] => type
                                    [label] => Type
                                    [key] => field_test_links_type
                                    [layout] => horizontal
                                )

                            [1] => Array
                                (
                                    [type] => text
                                    [name] => label
                                    [label] => Label
                                    [key] => field_test_links_label
                                    [conditional_logic] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [field] => field_test_links_type
                                                            [operator] => !=
                                                            [value] => none
                                                        )

                                                )

                                        )

                                )

                            [2] => Array
                                (
                                    [type] => page_link
                                    [name] => internal
                                    [label] => Internal
                                    [key] => field_test_links_internal
                                    [conditional_logic] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [field] => field_test_links_type
                                                            [operator] => ==
                                                            [value] => internal
                                                        )

                                                )

                                        )

                                )

                            [3] => Array
                                (
                                    [type] => url
                                    [name] => external
                                    [label] => External
                                    [key] => field_test_links_external
                                    [conditional_logic] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [field] => field_test_links_type
                                                            [operator] => ==
                                                            [value] => external
                                                        )

                                                )

                                        )

                                )

                            [4] => Array
                                (
                                    [type] => file
                                    [name] => file
                                    [label] => File
                                    [key] => field_test_links_file
                                    [conditional_logic] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [0] => Array
                                                        (
                                                            [field] => field_test_links_type
                                                            [operator] => ==
                                                            [value] => file
                                                        )

                                                )

                                        )

                                )

                        )

                )

        )

    [location] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [param] => post_type
                            [operator] => ==
                            [value] => page
                        )

                )

        )

)

Show Collapsed

Since the collapsed setting for the repeater field uses the field key, which is often generated automatically, how about providing a method to set a field within a repeater as the item visible when collapsed.


$client
   ->addRepeater( 'contacts' )
      ->addText( 'first_name')
      ->addText( 'last_name')
      ->addText('nickname')
      ->addText('position')
         ->setAsCollapsedItem()
      ->endRepeater()
   ->setLocation( 'post_type', '==', 'client' );

->addLayout should have attributes

I am building an import script where I can convert acf-json files to AcfGroups. Now I'm running into an issue with the addLayout field. In my original json file a Layout has a key, name, label and display. In the AcfGroups a layout can only have a name. Since the label of the layout is a translated string of the name it would be great when the label can still be applied to the field.

In the original json the Layout is described this way:

"layouts": [
                {
                    "key": "58fdc17c7498b",
                    "name": "page_anchor",
                    "label": "Verwijzing naar onderdeel",
                    "display": "block",
                    "sub_fields": [
                    ]
                }
]

My AcfGroup after converting is like this:

->addLayout( 'images_multiple', [
                'key' => "58e4ad59b4665",
                'label' => "Meerdere afbeeldingen",
                'display' => "block",
            ] )

The label doesn't show in the view anymore.

Is there a way to fix this?

Field is not found after migrating from acf-local josn files

Hi,

I'am migrating from local acf-json fields to acf-builder and now ACF can not recognized fields as a repeater field any more. Is gives just a sting with the count of the number of repeater items, like "2".

(sorry for the unstyled/formatted code but this way is better readeble)

JSON field config
{
"key": "field_57a989b435b9a",
"label": "Componisten en Composities",
"name": "event_composers_composition",
"type": "repeater",
"value": null,
"instructions": "Selecteer hier de componisten en composities die gespeeld worden op/tijdens dit event. Wanneer een componist of compositie nog niet bestaat kan deze via het plusje toegevoegd worden.",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"collapsed": "field_57a989da35b9b",
"min": 1,
"max": 0,
"layout": "table",
"button_label": "Nieuwe regel",
"sub_fields": [
{
"key": "field_57a989da35b9b",
"label": "Componist",
"name": "componist",
"type": "taxonomy",
"value": null,
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"taxonomy": "composer",
"field_type": "select",
"allow_null": 0,
"add_term": 1,
"save_terms": 1,
"load_terms": 0,
"return_format": "id",
"multiple": 0
},
{
"key": "field_57a98a0435b9c",
"label": "Compositie",
"name": "composition",
"type": "taxonomy",
"value": null,
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"taxonomy": "composition",
"field_type": "select",
"allow_null": 1,
"add_term": 1,
"save_terms": 1,
"load_terms": 0,
"return_format": "id",
"multiple": 0
}
]
},

ACF builder config
->addRepeater( 'event_composers_composition', [
'key' => "field_57a989b435b9a",
'label' => "Componisten en Composities",
'instructions' => "Selecteer hier de componisten en composities die gespeeld worden op/tijdens dit event. Wanneer een componist of compositie nog niet bestaat kan deze via het plusje toegevoegd worden.",
'collapsed' => "componist",
'layout' => "table",
'button_label' => "Nieuwe regel",
] )
->addField( 'componist', 'taxonomy', [
'key' => "field_57a989da35b9b",
'label' => "Componist",
'taxonomy' => "composer",
'field_type' => "select",
'add_term' => "1",
'save_terms' => "1",
'return_format' => "id",
] )
->addField( 'composition', 'taxonomy', [
'key' => "field_57a98a0435b9c",
'label' => "Compositie",
'taxonomy' => "composition",
'field_type' => "select",
'allow_null' => "1",
'add_term' => "1",
'save_terms' => "1",
'return_format' => "id",
] )
->endRepeater()

It looks like the key value is not use when registering the fields.

Choices array in args is misinterpreted.

There's an issue with choices where the choices array gets misinterpreted and the values of the labels for the choices overwrite the values.

$finished
   ->addRadio('finished',[
      'label' => 'Are you finished?',
      'allow_null' => true,
      'required' => true,
      'choices' => [
         'yes' => 'Yes, please send my report for review!',
         'no' => 'No, save my report for later completion.',
      ]
      ])
   ->setLocation( 'post_type', '==', 'work_order' );

print_r($finished->build());

Results in

Array
(
    [key] => group_finished
    [title] => Are you finished?
    [layout] => row
    [fields] => Array
        (
            [0] => Array
                (
                    [choices] => Array
                        (
                            [Yes, please send my report for review!] => Yes, please send my report for review!
                            [No, save my report for later completion.] => No, save my report for later completion.
                        )

                    [type] => radio
                    [name] => finished
                    [label] => Are you finished?
                    [key] => field_finished_finished
                    [allow_null] => 1
                    [required] => 1
                )

        )

    [location] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [param] => post_type
                            [operator] => ==
                            [value] => work_order
                        )

                )

        )

)

Unique Field Keys

There is an issue, especially when composing fields, where two fields will have the same key. This causes ACF to save only one field (the one appearing second) in the admin, because ACF uses the keys as the input tag names.

ACF solves this issue when using the UI by randomly generating the keys so they are unique.

We will solve this by namespacing a field's key by its parent field groups.

For example:

$builder = new FieldsBuilder('page_content');
$builder->addFlexibleContent('sections')
              ->addLayout('banner')
                  ->addText('title')
                  ->addWysiwyg('content')
              ->addLayout('content_columns')
                  ->addRepeater('columns', ['min' => 1, 'max' => 2])
                      ->addWysiwyg('content');

The group's key will become: group_page_content.
The flexible content areas sections key will become: field_page_content_sections
The layout banner key will become: field_page_content_sections_banner
And the title field's key in the banner will become: field_page_content_sections_banner_title

This provides a logical semantic naming scheme for keys. They will be unique because the names of fields need to be unique. It mirrors how ACF stores fields name in the database.

The last field in the above example will have the key: field_page_content_sections_content_columns_columns_content

A user of ACF Builder shouldn't have to worry about key names, but if the user ever needs to know what the key for a particular field is, they should be able to logically deduce it.

Add filter for auto generated labels, titles.

It would be great to have the ability to filter the auto generated labels and titles.

Often there are just a few simple changes I need to make, but they cross a wide number of fields.

How do you "break out" of a repeater

First off, thanks for the awesome tool. This is a much less verbose way of building ACF configs.

I want to build 2 main tabs, "Cards" and "CTA".

With the build below, CTA gets added inside the repeater.

In other words I expect this:

->addTab('Cards')
->addText('cards_title')
->addRepeater('cards_list', [
	'button_label' => 'Add Card',
	'layout' => 'row'])
->addImage('card_icon')
->addText('card_title')
->addWysiwyg('card_content')
->addTab('CTA')
->addWysiwyg('cta_1_main_content')
->addText('cta_1_last_line')

To display like:

->addTab('Cards')
	->addText('cards_title')
	->addRepeater('cards_list', [
	'button_label' => 'Add Card',
	'layout' => 'row'])
		->addImage('card_icon')
		->addText('card_title')
		->addWysiwyg('card_content')
->addTab('CTA')
	->addWysiwyg('cta_1_main_content')
	->addText('cta_1_last_line')

but it displays like:

->addTab('Cards')
	->addText('cards_title')
	->addRepeater('cards_list', [
	'button_label' => 'Add Card',
	'layout' => 'row'])
		->addImage('card_icon')
		->addText('card_title')
		->addWysiwyg('card_content')
		->addTab('CTA')
			->addWysiwyg('cta_1_main_content')
			->addText('cta_1_last_line')

Wondering if there is some sort of addEndOfGroup or some such.

Post object field doesn't find posts that do exist

after I created Post Object field with acf-builder, post object field didn't find any results (but posts did exist, but ajax returned results as false) .. That problem is related to PHP because it didn't occur after I switched to PHP 7.1 (from 7.0)
acf-builder v1.4.0
acf-pro v5.5.14

NB: I used custom post type.

class Doctrine dependency issue when using without composer.

When using the included autoload.php instead of composer, the repeaters and flexible fields fail when rewriting their 'Add Row' buttons.
I've tinkered with composer a little to try and properly set up the dependancies but I don't really know what I'm doing with it and don't have a lot of time to figure it out. It seems to download a lot of extra files that I don't need, particularly since this there are only two functions that rely on Doctrine.
Do you happen to have a link where I can download a compiled version that solves this problem?

Otherwise I'm just going to have to comment out those class function calls which isn't necessarily ideal for version control.

Aside from that this is a fantastic plugin, big improvement over working with the standard acf field arrays.

Updated to 1.2 and image fields stopped working

My image field variables are outputting image IDs instead of image objects now. Here is the code I am using.

$hero = new FieldsBuilder('hero');
$hero
  ->addImage('hero_image')
  ->setLocation('post_type', '==', 'page')

add_action('acf/init', function() use ($hero) {
  acf_add_local_field_group($hero->build());
});

I also tried adding 'return_format' => 'array', to the addImage arguments but it did not affect it.

Any thoughts?

Set location using page name

Not sure if it's possible but would be great to have something like this:

screenshot 2016-06-13 12 06 35

Would it be possible to set a location like this?

->setLocation('page', '==', 'home')

Well, I think we could do something like this:

$page = get_page_by_path('home');

$rows = new FieldsBuilder('rows');
$rows
  ->addText('title')
  ->setLocation('page', '==', $page->ID);

But there's probably a better way. (Not sure what ACF does internally)

Extention of FieldBuilder parent class

Hi Steve,

First of all, thanks for a cool extension for ACF.

We start using this extension a while ago and faced with a few problems.

  1. We wrote a small class, which can register fields like this (If you like we can share this code. It allows re-use field definitions and modules definitions without any includes and keep code very clean):
    http://prntscr.com/k1xqfy
  • $this->add() just add it to internal $fields property, which then used to register fields through acf_add_local_field_group()
  • $this->builder() just return an instance of Fields_Builder() class.

So you can add several fields group like: $this->add( $this->builder('group1')->... , $this->builder('group2')->... )

However in this way we stuck, that when we add fields we get wrong class instance at the end (we get FieldBuilder instance, not a FieldsBuilder). We wrote small end() method in our own child class. It looks like this:

	/**
	 * @return Fields_Builder
	 */
	public function end() {
		return $this->getParentContext() ? $this->getParentContext()->end() : $this;
	}

So it's very similar to endFlexibleContent() method. But it's global.

  1. We use styling of fields to better organize edit interface on different screen resolution.
    It's too much to write setConfig('wrapper', ['some config']) for each field, when all configs are similar. Code becomes to hard to read, because 70% of code goes to wrapper config array definitions. For each field class you have separate class and we can't overwrite FieldsBuilder to patch anything in the same way.
    We can make a pull request to your repository with method like setWrapper($width, $class, $id) inside parent FieldBuilder class (we are playing with the final syntax right now to make it easy to use/read). So we can call something like:
$this->builder('some group')
    ->addText('my_field')
        ->setWrapper('30%')
   ->addText('another_field)
       ->setWrapper('70%')
    ...

Are you interesting in such patches? We can deliver them to you this week.

Field modification API

Create an API that can be used to modify existing fields in a FieldsBuilder, including the ability to remove a field.

I'm thinking of creating the methods:

/**
 * Modify an existing field
 * @param $name string            Field Name
 * @param $modifications closure  Closure that is passed a new Builder with the 
 *                                field  to be modified already initialized.
 *                                Modifications can be made and then returned. The new
 *                                field config will replace the existing.
 * @return $this FieldsBuilder
 */
function modifyField($name, $modifications);

/**
 * Remove an existing field
 * @param $name string   Field Name
 * @return $this FieldsBuilder
 */
function removeField($name);

The more interesting method is the modifyField. By passing in a function to modify the single field, we can isolate it without having to maintain an internal cursor on the FieldsBuilder. The fields config from this ModifyBuilder will replace the existing field config. This will even allow you to add new fields after a particular field.

Example:

$background = new FieldsBuilder('Background');
$background
    ->addImage('background_image')
    ->addColorPicker('background_color');

$banner = new FieldsBuilder('Banner');
$banner
    ->addText('title')
    ->addFields($background)
    ->modifyField('background_image', function($modifyBuilder) {
        return $modifyBuilder
            ->instructions('Upload an image for the background of the banner')
            ->addTrueFalse('background_fixed')
                ->instructions('Check if the background should be a fixed position');
     });

This would add some instructions to the background_image field as well as insert a new background_fixed field between the background_image and background_color fields.

How are you using it?

Before anything else, thank you very much for this awesome lib, it really prevents a lot of headaches ❤️ .

I was wondering about how people are using acf-builder. Where the fields and (possibly) partial fields are located and where are they loaded?

My current folder structure is heavily inspired by Sage. I have a app/fields directory inside my theme with each of its files returning one or more FieldBuilders. The script in charge of loading (acf_add_local_field_group) the fields is inside the setup.php (part of my functions.php)

Folder structure:

├── app/                  # → Theme PHP
│   ├── fields/           # → ACF Fields
│   ├── fields/partials/  # → Partial ACF fields, for include/require
│   └── setup.php         # → Read and load all fields in 'app/fields'

setup.php

/**
 * ACF Builder initialization and fields loading
 */
define('FIELDS_DIR', dirname(__FILE__) . '/fields');
if (is_dir(FIELDS_DIR)) {
    add_action('acf/init', function () {
        foreach (glob(FIELDS_DIR . '/*.php') as $file_path) {
            if (($fields = require_once $file_path) !== true) {
                if (!is_array($fields)) {
                    $fields = [$fields];
                }
                foreach ($fields as $field) {
                    if ($field instanceof FieldsBuilder) {
                        acf_add_local_field_group($field->build());
                    }
                }
            }
        }
    });
}

Sample field app/fields/sample.php

$sample_field = new FieldsBuilder('sample_field');
$sample_field
    ->addText('title')
    ->addWysiwyg('content')
    ->setLocation('post_type', '==', 'page')
        ->or('post_type', '==', 'post');

return $sample_field;

Note: I haven't tested this a lot and probably the hook can be optimized (maybe switching from glob() to scandir()?).

required, instructions, and defaultValue methods are mis-documented

Wiki documentation shows these methods as required(), instructions(), and defaultValue(), but should be setRequired(), setInstructions(), and setDefaultValue().

Otherwise they throw an exception of "No method exists."

The prefix 'set' is more inline with the setConfiguration() method but conditional() has no prefix and I'm all for less typing where ever possible so I vote for some wrapper methods of some kind.

Fields arguments docs

I'm currently using this article to know which arguments a certain field accepts.

Maybe it would be good to have it's content on the wiki or something alike (I'm reluctant to depend on an external link that can go off anytime...).

Edit: The official ACF "guide" is missing a lot of info.

Hide on screen

I can't find any documentation on this, how do I use the 'Hide on Screen' ACF functionality.

$banner = new StoutLogic\AcfBuilder\FieldsBuilder('banner', [
    'title' => 'Banner',
    'style' => 'seemless',
    'hide_on_screen' => [
        'content_editor',
        'permalink'
    ]
]);

Is there a quick option similar to toggle all?

setLocation not working with custom location

Hi.

First of all, thank you very much for acf-builder. It's absolutely amazing and made my life much more sane.

I'm not sure if you've heard of the plugin WP Papi -- but I wrote a set of filters so I could use "Papi Page Types" inside of ACF.

For some reason my filters work perfectly in ACF's Field Builder but when I try to use it with acf-builder it doesn't work.

setLocation('papi_page_type', '==', 'home')

Here are my filters:

add_filter('acf/location/rule_types', function ($choices) {
    $choices['Papi']['papi_page_type'] = 'Page Type';
    return $choices;
});

add_filter('acf/location/rule_values/papi_page_type', function ($choices) {
    $page_types = papi_get_all_page_types();

    if ($page_types) {
        $page_types = array_map(function($page_type) {
            return [
                'id'   => $page_type->get_id(),
                'name' => $page_type->name
            ];
        }, $page_types);

        foreach ($page_types as $page_type) {
            $choices[$page_type['id']] = $page_type['name'];
        }
    }

    return $choices;
});

add_filter('acf/location/rule_match/papi_page_type', function ($match, $rule, $options) {
    if (!$options['post_id']) {
        return false;
    }

    $page_type = papi_get_page_type_id($options['post_id']);
    $selected_type = $rule['value'];

    if ($rule['operator'] == '==') {
        $match = ($page_type == $selected_type);
    }

    if ($rule['operator'] == '!=') {
        $match = ($page_type != $selected_type);
    }

    return $match;
}, 10, 3);

Since it works when creating the fields in ACF's builder, I'm not really sure why it wouldn't be working with acf-builder.

Any help with this would be appreciated.

Conditionals don't work when checking value of a parent field.

When trying to add a conditional from a field in a Flexible Layout which depends on the value of a field that is a sibling to the Flexible Content field, it fails with a FieldNotFoundException.

Example:

A slider where normally only the background changes and the content stays fixed. But the option can be set that each slide has its own content.

$builder = new FieldsBuilder('slider');
$slider
    ->addTrueFalse('enable_slide_content')
            ->setInstructions('By default only the background will change on the slider, while the content of the first slide will remain. If you want the content to change with each slide, enable this option.')
    ->addWysiwyg('slider_content')
        ->conditional('enable_slide_content', '!=', '1')
    ->addFlexibleContent('slides')
         ->addLayout('slide')
              ->addImage('background')
              ->addWysiwyg('content')
                   ->conditional('enable_slide_content', '==', '1');
$builder->build()

We will get a FieldNotFoundException due to enable_slide_content not being found in the slide layout. But this is valid ACF.

Solution:

When a field isn't found, we need to traverse up the builder tree and check for the field name at each ancestor builder before throwing the exception. That is the easy part.

The hard part is doing the same in the NamespaceFieldKey transformation. Because we want the namespaced key to be that of the ancestor field, not the child field.

Potentially a hard bug to fix, might require significant rewrite. But would be a really useful feature.

Field name error

I'm running into an issue with this code...

    $documents = new FieldsBuilder( 'documents');
    $documents
        ->addText('documents_title')
        ->addRepeater( 'documents', [
            'layout' => 'block',
        ])

ACF Builder seems to get confused when you are using the field group name as part of the field name. "documents_title" ends up getting the field name of "title".

screen shot 2017-05-23 at 9 54 04 am

Clone? Extension plugins?

Great little bit of code you have here!

Two questions, more than issues...

  1. ACF Pro now has the "Clone" type and it's pretty awesome. Is there some logic to use commandeering this for "object inheritance", do you think? Or is your approach enough? I just assume using the default ACF approach is typically wise??

  2. I use ACF Pro extensions for things like choosing a GravityForm, or a registered menu for insertion on the page somehow... Would it be difficult to have a simple extension framework for acf-builder that would allow me to use these, or even better, have some convention where they're auto-discovered and usable immediately??!

Many thanks!

-Alister Cameron

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.