EasyAdminBundle icon indicating copy to clipboard operation
EasyAdminBundle copied to clipboard

An entityFilter (not equal) doesn't work correctly when user can choose more than one option

Open Mis0u opened this issue 3 years ago • 3 comments

EntityFilter works well when you can select only one option but in my case, I allow the user to choose severals option :

Capture d’écran 2022-06-14 à 11 37 49

Now if I filter by not equal James Harveyd this is my result :

Capture d’écran 2022-06-14 à 11 38 04

I don't understand why EA make appear the second line, any idea ?

Mis0u avatar Jun 14 '22 09:06 Mis0u

How do you make EntityFilter suitable for multiple selections? As far as I can see only one can be selected

parijke avatar Jun 14 '22 16:06 parijke

@parijke You can choose multiple options if you are in ManyToMany

Mis0u avatar Jun 17 '22 08:06 Mis0u

It's hard to make sql query. I made another filter, that works fine for me.

Usage:

$filters->add(EntityAssocFilter::new('properties', 'value')
    ->setLabel('Properties')
    ->setChoices($this->propertyValueRepository->findAllChoices())
    ->canSelectMultiple(true)
    ->setFormTypeOption('value_type_options.attr.data-widget', null)
    ->setFormTypeOption('value_type_options.attr.size', 10)
);

Code:

use Doctrine\ORM\Query\Expr\Orx;
use Doctrine\ORM\QueryBuilder;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Filter\FilterInterface;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\FilterDataDto;
use EasyCorp\Bundle\EasyAdminBundle\Filter\FilterTrait;
use EasyCorp\Bundle\EasyAdminBundle\Form\Filter\Type\ChoiceFilterType;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\ComparisonType;

final class EntityAssocFilter implements FilterInterface
{
    use FilterTrait;

    private string $assocPropertyName;


    public static function new(string $propertyName, string $assocPropertyName, $label = null): self
    {
        return (new self())
            ->setFilterFqcn(__CLASS__)
            ->setProperty($propertyName)
            ->setAssocPropertyName($assocPropertyName)
            ->setLabel($label)
            ->setFormType(ChoiceFilterType::class)
        ;
    }

    public function getAssocPropertyName(): string
    {
        return $this->assocPropertyName;
    }

    public function setAssocPropertyName(string $assocPropertyName): self
    {
        $this->assocPropertyName = $assocPropertyName;

        return $this;
    }

    public function setChoices(array $choices): self
    {
        $this->dto->setFormTypeOption('value_type_options.choices', $choices);

        return $this;
    }

    public function renderExpanded(bool $isExpanded = true): self
    {
        $this->dto->setFormTypeOption('value_type_options.expanded', $isExpanded);

        return $this;
    }

    public function canSelectMultiple(bool $selectMultiple = true): self
    {
        $this->dto->setFormTypeOption('value_type_options.multiple', $selectMultiple);

        return $this;
    }

    public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
    {
        $alias = $filterDataDto->getEntityAlias();
        $property = $filterDataDto->getProperty();
        $comparison = $filterDataDto->getComparison();
        $parameterName = $filterDataDto->getParameterName();
        $value = $filterDataDto->getValue();
        if (!is_array($value)) {
            $value = [$value];
        }
        $isMultiple = $filterDataDto->getFormTypeOption('value_type_options.multiple');

        $assocAlias = $filterDataDto->getParameterName();
        $assocProperty = $this->assocPropertyName;

        if ($entityDto->isToManyAssociation($property)) {
            if (0 === \count($value)) {
                $queryBuilder->leftJoin(sprintf('%s.%s', $alias, $property), $assocAlias);
                $queryBuilder->andWhere(sprintf('%s.%s %s', $assocAlias, $assocProperty, $comparison));
            } else {
                $queryBuilder->leftJoin(sprintf('%s.%s', $alias, $property), $assocAlias,
                    'WITH', "$assocAlias.$assocProperty IN (:$parameterName)")
                    ->setParameter($parameterName, $value);
                if ('IN' === $comparison) {
                    $queryBuilder->andWhere(sprintf('%s.%s IS NOT NULL', $assocAlias, $assocProperty));
                }
                if ('NOT IN' === $comparison) {
                    $queryBuilder->andWhere(sprintf('%s.%s IS NULL', $assocAlias, $assocProperty));
                }
            }
        } else if (null === $value || ($isMultiple && 0 === \count($value))) {
            $queryBuilder->andWhere(sprintf('%s.%s %s', $assocAlias, $assocProperty, $comparison));
        } else {
            $orX = new Orx();
            $orX->add(sprintf('%s.%s %s (:%s)', $assocAlias, $assocProperty, $comparison, $parameterName));
            if (ComparisonType::NEQ === $comparison) {
                $orX->add(sprintf('%s.%s IS NULL', $assocAlias, $assocProperty));
            }
            $queryBuilder->andWhere($orX)
                ->setParameter($parameterName, $value);
        }
    }
}

maxkain avatar Jun 17 '22 09:06 maxkain