vinkla / extended-acf

Register advanced custom fields with object-oriented PHP
MIT License
455 stars 61 forks source link

register multiple field in a foreach #107

Closed azralth closed 2 years ago

azralth commented 2 years ago

Hi,

I'm adding a new plugin when i want to automate acf field creation. I have an array like this : `

    $acf_fields = [
        __('Information du coach') => [
            [
                'type' => 'Text',
                [
                    'unique_id' => 'coach_name',
                    'title' => __('Nom'),
                    'description' => __('Le nom du coach'),
                    'required' => true
                ]
            ],
            [
                'type' => 'Text',
                [
                    'unique_id' => 'coach_firstname',
                    'title' => __('Prénom'),
                    'description' => __('Le prénom du coach'),
                    'required' => true
                ]
            ],
        ]
    ];

`

And then the creation method like this : `

    foreach ($acf_fields as $group => $def) {
        register_extended_field_group([
            'title' => $group,
            'fields' => [
                $this->registerNewFields($def),
            ],
            'location' => [
                Location::if('post_type', $location)
            ],
        ]);
    }`

the registerNewFields :

`

    Text::make($def['title'], $def['unique_id'])
        ->instructions($def['description'])
        ->required(true);

`

But this is not working, i get ( ! ) Fatal error: Uncaught Error: Call to a member function setParentKey() on null in D:\web\Yhellow\WordPress\vendor\wordplate\acf\src\FieldGroup.php on line 49

I don't know how to loop in fields and create with your class.

Thanks for your help

fiskhandlarn commented 2 years ago

@azralth

  1. what's on line 49?
  2. what happens if you remove the function call to registerNewFields and put the Text::make statement directly in the foreach loop? afaik that function should at least return something (right now i'm guessing 'fields' is set to an empty array).
azralth commented 2 years ago

Hi,

Now it works, but the foreach stops at the first iteration. I can only get my first field. Usually, we write like this :

Text::make('Title', 'heading') ->instructions('Add the text value') ->required(), Text::make('Title', 'heading') ->instructions('Add the text value') ->required() They don't end with a ";"

But when we try to create inside a loop, i don't know why, the iteration stop after the first result. I'm in about hours now and tired to not understand what is happening ...

If i remove the registerNewFields and put directly the Text:make, it works perfectly. I get my two custom field.

Again, the problem now is that the foreach stop after the first iteration return. The $fields contain my two field 'coach_name' and 'coach_firstname', but i can get only the first. Thinks the return break the loop. That is really weird.

Here the entier code :

` public static $authorized_fields = [ 'Text', 'Textarea', 'Email' ];

public function __construct(array $acfFields, $location)
{
    $this->registerNewGroup($acfFields, $location);
}

public function registerNewGroup(array $acf_fields, string $location)
{
    foreach ($acf_fields as $group => $def) {
        register_extended_field_group([
            'title' => $group,
            'fields' => [
                $this->registerNewFields($def)
            ],
            'location' => [
                Location::if('post_type', $location)
            ],
        ]);
    }
}

private function registerNewFields(array $fields)
{
    foreach ($fields as $field) {
        $type = $field['type'];
        if ($this->isValidType($type)) {
            $method = 'registerField' . $type;
            return $this->$method($field[0]);
        }
    }
}

/**
 * Check if is a valid type
 */
private function isValidType(string $type): bool
{
    return in_array($type, AcfFields::$authorized_fields);
}

public function registerFieldText(array $def)
{
    return Text::make($def['title'], $def['unique_id'])
        ->instructions($def['description'])
        ->required(true);
}

`

fiskhandlarn commented 2 years ago

@azralth

But when we try to create inside a loop, i don't know why, the iteration stop after the first result.

Can you paste the new code where this is happening? Because foreach ($acf_fields as $group => $def) should run twice with the $acf_fields in OP.

If i remove the registerNewFields and put directly the Text:make, it works perfectly. I get my two custom field.

So what is the problem? :)

azralth commented 2 years ago

Here the code :

`

    $acf_fields = [
        __('Information du coach') => [
            [
                'type' => 'Text',
                [
                    'unique_id' => 'coach_firstname',
                    'title' => __('Prénom'),
                    'description' => __('Le prénom du coach'),
                    'required' => true
                ]
            ],
            [
                'type' => 'Text',
                [
                    'unique_id' => 'coach_name',
                    'title' => __('Nom'),
                    'description' => __('Le nom du coach'),
                    'required' => true
                ]
            ],
        ]
    ];

`

`

public static $authorized_fields = [
    'Text', 'Textarea', 'Email'
];

public function __construct(array $acfFields, $location)
{
    $this->registerNewGroup($acfFields, $location);
}

public function registerNewGroup(array $acf_fields, string $location)
{
    foreach ($acf_fields as $group => $def) {
        register_extended_field_group([
            'title' => $group,
            'fields' => [
                $this->registerNewFields($def)
            ],
            'location' => [
                Location::if('post_type', $location)
            ],
        ]);
    }
}

private function registerNewFields(array $fields)
{
    $result = [];
    foreach ($fields as $field) {
        $type = $field['type'];
        if ($this->isValidType($type)) {
            $method = 'registerField' . $type;
            return $this->$method($field[0]);
        }
    }
}

/**
 * Check if is a valid type
 */
private function isValidType(string $type): bool
{
    return in_array($type, AcfFields::$authorized_fields);
}

public function registerFieldText(array $def)
{
    return Text::make($def['title'], $def['unique_id'])
        ->instructions($def['description'])
        ->required(true);
}

`

the foreach ($acf_fields as $group => $def) loop trought the group field and just after the registerNewFields loop trhought the field. And i can only get the first field.

I need to automate acf field creation like this because i'm creating a large plugin with a lot of custom_post. And i don't want to create all the field inside them. That why i try to built something more flexible.

fiskhandlarn commented 2 years ago

@azralth your foreach will only loop once since $acf_fields only has one item. but registerNewFields will always return one and only one item because of the return exiting the foreach loop. try the below instead (and please not the changes in both methods).

    public function registerNewGroup(array $acf_fields, string $location)
    {
        foreach ($acf_fields as $group => $def) {
            register_extended_field_group([
                'title' => $group,
                'fields' => $this->registerNewFields($def),
                'location' => [
                    Location::if('post_type', $location)
                ],
            ]);
        }
    }

    private function registerNewFields(array $fields): array
    {
        $result = [];
        foreach ($fields as $field) {
            $type = $field['type'];
            if ($this->isValidType($type)) {
                $method = 'registerField' . $type;
                $result []= $this->$method($field[0]);
            }
        }

        return $result;
    }
fiskhandlarn commented 2 years ago

Eitherway, this is not an issue with wordplate/extended-acf. This should be safe to close, @vinkla.

azralth commented 2 years ago

Hi,

Thanks for your help. I've already try this approach. Add in an array an return it. But i get Fatal error: Uncaught Error: Call to a member function setParentKey() . Eitheirway, I dropped it and I create directly the fields in the class of the entity.

fiskhandlarn commented 2 years ago

@azralth have you tried with all the code i wrote above? i did and got no errors. i got the same error when i had 'fields' => [$this->registerNewFields($def)], (the code you had) but not with 'fields' => $this->registerNewFields($def),