vinkla / extended-acf

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

Conditional logic with or #95

Closed LudovicFauchet closed 3 years ago

LudovicFauchet commented 3 years ago

Hello here !

Thanks for this beautiful plugin ! I have a question i'm using a conditional logic on a field but the conditional logic could check if the value of the previous field equal one value or one other.

\WordPlate\Acf\Fields\Radio::make('Couleur associée', 'archive_color')
            ->conditionalLogic([
                \WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('cat'),
                WordPlate\Acf\ConditionalLogic::or('archive_type')->equals('dis_sport')
            ])
            ->required()

I tried that but it doesn't work yet.

How to do that ?

Cheers

vinkla commented 3 years ago

Thanks for this beautiful plugin!

Thanks for letting us know 🙌

I tried that but it doesn't work yet.

I can't see the archive_type field in your example?

LudovicFauchet commented 3 years ago

Here my full group code :

register_extended_field_group([
    'title'    => 'Catégorie associée',
    'hide_on_screen' => ['the_content', 'revisions', 'featured_image'],
    'style' => 'block',
    'location' => [
        \WordPlate\Acf\Location::if('page_template', 'tpl-fake-archive.php')
    ],
    'fields'   => [
        \WordPlate\Acf\Fields\Radio::make('Type d\'archive', 'archive_type')
            ->choices([
                'cat' => 'Catégorie',
                'loc' => 'Localisation',
                'dis_sport' => 'Discipline sport',
            ])
            ->defaultValue('cat')
            ->layout('horizontal') // vertical or horizontal
            ->required(),
        \WordPlate\Acf\Fields\Taxonomy::make('Localisation associée', 'archive_location')
            ->conditionalLogic([
                \WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('loc')
            ])
            ->taxonomy('localisations')
            ->appearance('select')
            ->createTerms(false)
            ->loadTerms(true)
            ->saveTerms(true)
            ->returnFormat('id')
            ->required(),
        \WordPlate\Acf\Fields\Taxonomy::make('Categorie associée', 'archive_category')
            ->conditionalLogic([
                \WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('cat')
            ])
            ->taxonomy('category')
            ->appearance('select')
            ->createTerms(false)
            ->loadTerms(true)
            ->saveTerms(true)
            ->returnFormat('id')
            ->required(),
        \WordPlate\Acf\Fields\Taxonomy::make('Discipline sport associée', 'archive_dis_sport')
            ->conditionalLogic([
                \WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('dis_sport')
            ])
            ->taxonomy('disciplines_sports')
            ->appearance('select')
            ->createTerms(false)
            ->loadTerms(true)
            ->saveTerms(true)
            ->returnFormat('id')
            ->required(),
        \WordPlate\Acf\Fields\Radio::make('Couleur associée', 'archive_color')
            ->conditionalLogic([
                \WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('cat'),
                //\WordPlate\Acf\ConditionalLogic::or('archive_type')->equals('dis_sport')
            ])
            ->required()
    ]
]);
vinkla commented 3 years ago

There is no or method, you will need to run if twice.

\WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('cat'),
\WordPlate\Acf\ConditionalLogic::if('archive_type')->equals('dis_sport')

This could actually be a feature idea, for those who want to chain instead of creating new conditional logic instances.

LudovicFauchet commented 3 years ago

Hello @vinkla this solution doesn't work, it doesn't display the field at all.

vinkla commented 3 years ago

Then I don't know. If you find a bug, please open a pull request with a fix.

LudovicFauchet commented 3 years ago

It's not a bug it's a missing feature, i don't know how to add this feature... But this is a problem i need this feature to develop my website

puredazzle commented 3 years ago

@LudovicFauchet you could use notEquals until we have implemented chaining for this function.

\WordPlate\Acf\Fields\Radio::make('Couleur associée', 'archive_color')
    ->conditionalLogic([
        \WordPlate\Acf\ConditionalLogic::if('archive_type')->notEquals('loc')
    ])
    ->required();
vinkla commented 3 years ago

Now that I think about it and read about @puredazzle solution. I'm not sure using two equals will work.

@LudovicFauchet have you tried setting up the same fields in the UI?

ogorzalka commented 1 year ago

Hi !

It would be really cool to have the and() and or() methods to manage field conditions.

Currently, for the needs of a project, we managed it in a "tricky" way.

We have a custom ConditionalField class with the following code:

<?php

namespace App\ThirdParty\ExtendedAcf;

class ConditionalLogic
{
    private array $conditions = [];

    public function __construct(private string $field, private string $operator, private string $value)
    {
        $this->conditions[] = [
            [
                'field' => $this->field,
                'operator' => $this->operator,
                'value' => $this->value,
            ],
        ];
    }

    public static function where(string $field, string $operator, string $value): self
    {
        return new static($field,  $operator, $value);
    }

    public function and(string $field, string $operator, string $value): self
    {
        $this->conditions[count($this->conditions) - 1][] = [
            'field' => $field,
            'operator' => $operator,
            'value' => $value,
        ];

        return $this;
    }

    public function or(string $field, string $operator, string $value): self
    {
        $this->conditions[] = [
            [
                'field' => $field,
                'operator' => $operator,
                'value' => $value,
            ],
        ];

        return $this;
    }

    public function get(): array
    {
        return $this->conditions;
    }
}

Then, to transform field names into field keys, we used the ACF hook acf/prepare_field (we added the ability to interact with other groups of fields present on the same edit page by prefixing it (e.g. my_other_group/my_field).

add_filter('acf/prepare_field', function ($field) {
    if (! isset($field['conditional'])) {
        return $field;
    }

    if (is_array($field['conditional'])) {
        return $field;
    }

    $conditional = $field['conditional']->get();

    $parentKey = $this->key();

    $field['conditional_logic'] = $conditional;

    array_walk_recursive($field['conditional_logic'], function (&$item, $key) use ($parentKey) {
        if ($key === 'field') {
            $item = $this->generateFieldKey($item, $parentKey);
        }
    });

    unset($field['conditional']);

    return $field;
});

To call it, we created an additional "conditional" parameter that then rewrites the "conditional_rules" array key.

$conditionalLogic = ConditionalLogic::where('item_type', '==', 'news')
    ->or('item_type', '==', 'video')
    ->or('item_type', '==', 'image');

$acfField = [
    // ...
    'conditional' => $conditionalLogic,
];

ConditionalLogic::where('item_type', '==', 'news')
    ->and('theme', '==', 'book')
    ->or('item_type', '==', 'video')
    ->and('theme', '==', 'games')
    ->or('item_type', '==', 'image');

It's not the most optimized way for keys, but it could provide a basic implementation? :)

vinkla commented 1 year ago

Looks like a cool implementation Olivier! I wonder if it is possible to add this feature without having to call the acf/prepare_field filter? Since we don't ship this package as a plugin it could be tricky to implement the filter.

ogorzalka commented 1 year ago

Yes, it's just a code used for the needs of a project where we needed to use these complex conditions. I will see if I can adapt it based on the existing code 🙂