diff --git a/doc/filters.rst b/doc/filters.rst index 34f0314e48..fe2f1bfd80 100644 --- a/doc/filters.rst +++ b/doc/filters.rst @@ -189,14 +189,30 @@ You can now use this custom filter in any of your dashboards and CRUD controller } } +Filters on associated entities +------------------------------- +(starting from version 3.x) +If you need to filter by the property of a related entity (e.g. +an ``order`` is associated with a ``customer`` and you want to filter orders by +the ``last_visit`` property of the ``customer``), just use the dot notation to get to the property + + public function configureFilters(Filters $filters): Filters + { + return $filters + // just as a string + ->add('customer.last_visit_date') + // or with an explicit type, either built-in or custom* + ->add(DateTimeFilter::new('customer.last_visit_date') + ; + } + +* Please note that, if you have pre-3.x custom filters, you'll have to update them to be able +to work like this. + Unmapped Filters ---------------- -By default, each filter must be associated with a property of the entity. -However, sometimes you need to filter by the property of a related entity (e.g. -an ``order`` is associated with a ``customer`` and you want to filter orders by -the ``country`` property of the ``customer``). In those cases, set the -``mapped`` option to ``false`` in the filter or you'll see an exception:: +In more complex cases, set the ``mapped`` option to ``false`` in the filter or you'll see an exception:: namespace App\Controller\Admin; @@ -214,9 +230,10 @@ the ``country`` property of the ``customer``). In those cases, set the return $filters // 'country' doesn't exist as a property of 'Order' so it's // defined as 'not mapped' to avoid errors - ->add(CustomerCountryFilter::new('country')->mapped(false)) + ->add(CustomerCountryFilter::new('country')->setFormTypeOptions('mapped', false)) ; } } .. TODO: explain and show an example of compound filter forms +.. TODO : separate how filter is rendered (the filter form) and how it will go get the property diff --git a/src/Dto/FilterDataDto.php b/src/Dto/FilterDataDto.php index 1ed2fe4d98..9405ee263a 100644 --- a/src/Dto/FilterDataDto.php +++ b/src/Dto/FilterDataDto.php @@ -42,6 +42,11 @@ public function getProperty(): string return $this->filterDto->getProperty(); } + public function getName(): string + { + return $this->filterDto->getName(); + } + public function getFormTypeOption(string $optionName) { return $this->filterDto->getFormTypeOption($optionName); diff --git a/src/Dto/FilterDto.php b/src/Dto/FilterDto.php index 1d75dd5c34..b460bbffa2 100644 --- a/src/Dto/FilterDto.php +++ b/src/Dto/FilterDto.php @@ -16,6 +16,7 @@ final class FilterDto private $propertyName; private $label; private $applyCallable; + private $name; public function __construct() { @@ -75,6 +76,10 @@ public function getProperty(): string public function setProperty(string $propertyName): void { $this->propertyName = $propertyName; + if (is_null($this->name)) { + $parts = explode('.', $propertyName); + $this->name = end($parts); + } } /** @@ -85,6 +90,18 @@ public function getLabel() return $this->label; } + public function getName(): string + { + return $this->name; + } + + public function setName(string $name) + { + $this->name = $name; + return $this; + } + + /** * @param string|false|null $label */ diff --git a/src/Filter/ArrayFilter.php b/src/Filter/ArrayFilter.php index 3a4cdf47ad..db9fb6b0d2 100644 --- a/src/Filter/ArrayFilter.php +++ b/src/Filter/ArrayFilter.php @@ -47,6 +47,7 @@ public function canSelectMultiple(bool $selectMultiple = true): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { $alias = $filterDataDto->getEntityAlias(); + // TODO : if filter property is compound, traverse the main property to get to the good one $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); $parameterName = $filterDataDto->getParameterName(); diff --git a/src/Filter/BooleanFilter.php b/src/Filter/BooleanFilter.php index dec081168a..cf10ec8598 100644 --- a/src/Filter/BooleanFilter.php +++ b/src/Filter/BooleanFilter.php @@ -29,6 +29,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $queryBuilder ->andWhere(sprintf('%s.%s %s :%s', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty(), $filterDataDto->getComparison(), $filterDataDto->getParameterName())) ->setParameter($filterDataDto->getParameterName(), $filterDataDto->getValue()); diff --git a/src/Filter/ChoiceFilter.php b/src/Filter/ChoiceFilter.php index ddcb165b50..2b00cf406a 100644 --- a/src/Filter/ChoiceFilter.php +++ b/src/Filter/ChoiceFilter.php @@ -52,6 +52,7 @@ public function canSelectMultiple(bool $selectMultiple = true): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Filter/ComparisonFilter.php b/src/Filter/ComparisonFilter.php index d55865a7f6..caec515aee 100644 --- a/src/Filter/ComparisonFilter.php +++ b/src/Filter/ComparisonFilter.php @@ -29,6 +29,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Filter/Configurator/ComparisonConfigurator.php b/src/Filter/Configurator/ComparisonConfigurator.php index 46df78d709..8e6ef85d34 100644 --- a/src/Filter/Configurator/ComparisonConfigurator.php +++ b/src/Filter/Configurator/ComparisonConfigurator.php @@ -24,6 +24,7 @@ public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $e public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $propertyType = $entityDto->getPropertyMetadata($filterDto->getProperty())->get('type'); if (Types::DATEINTERVAL === $propertyType) { diff --git a/src/Filter/Configurator/DateTimeConfigurator.php b/src/Filter/Configurator/DateTimeConfigurator.php index 6e72c06f6a..ffb2e890f6 100644 --- a/src/Filter/Configurator/DateTimeConfigurator.php +++ b/src/Filter/Configurator/DateTimeConfigurator.php @@ -25,6 +25,7 @@ public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $e public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $propertyType = $entityDto->getPropertyMetadata($filterDto->getProperty())->get('type'); if (Types::DATE_MUTABLE === $propertyType) { diff --git a/src/Filter/Configurator/EntityConfigurator.php b/src/Filter/Configurator/EntityConfigurator.php index b2e26b4e8c..9e84e12fbf 100644 --- a/src/Filter/Configurator/EntityConfigurator.php +++ b/src/Filter/Configurator/EntityConfigurator.php @@ -22,6 +22,7 @@ public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $e public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $propertyName = $filterDto->getProperty(); if (!$entityDto->isAssociation($propertyName)) { return; diff --git a/src/Filter/Configurator/NumericConfigurator.php b/src/Filter/Configurator/NumericConfigurator.php index 68bb1a9cc2..972877a409 100644 --- a/src/Filter/Configurator/NumericConfigurator.php +++ b/src/Filter/Configurator/NumericConfigurator.php @@ -24,6 +24,7 @@ public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $e public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $propertyType = $entityDto->getPropertyMetadata($filterDto->getProperty())->get('type'); if (Types::DECIMAL === $propertyType) { diff --git a/src/Filter/Configurator/TextConfigurator.php b/src/Filter/Configurator/TextConfigurator.php index 3ffda85e07..5d284cd5cc 100644 --- a/src/Filter/Configurator/TextConfigurator.php +++ b/src/Filter/Configurator/TextConfigurator.php @@ -24,6 +24,7 @@ public function supports(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $e public function configure(FilterDto $filterDto, ?FieldDto $fieldDto, EntityDto $entityDto, AdminContext $context): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $propertyType = $entityDto->getPropertyMetadata($filterDto->getProperty())->get('type'); if (Types::JSON === $propertyType) { diff --git a/src/Filter/DateTimeFilter.php b/src/Filter/DateTimeFilter.php index 6d4b54c503..6bec847dc7 100644 --- a/src/Filter/DateTimeFilter.php +++ b/src/Filter/DateTimeFilter.php @@ -30,6 +30,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Filter/EntityFilter.php b/src/Filter/EntityFilter.php index 01a48f3bb4..704a92659d 100644 --- a/src/Filter/EntityFilter.php +++ b/src/Filter/EntityFilter.php @@ -31,6 +31,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Filter/FilterTrait.php b/src/Filter/FilterTrait.php index c89c37344e..a78b109a3a 100644 --- a/src/Filter/FilterTrait.php +++ b/src/Filter/FilterTrait.php @@ -28,7 +28,7 @@ private function __construct() public function __toString(): string { - return $this->dto->getProperty(); + return $this->dto->getName(); } public function setFilterFqcn(string $fqcn): self @@ -38,6 +38,12 @@ public function setFilterFqcn(string $fqcn): self return $this; } + public function setName(string $name): self { + $this->dto->setName($name); + + return $this; + } + public function setProperty(string $propertyName): self { $this->dto->setProperty($propertyName); diff --git a/src/Filter/NumericFilter.php b/src/Filter/NumericFilter.php index de34c5f0b2..41b630a8ff 100644 --- a/src/Filter/NumericFilter.php +++ b/src/Filter/NumericFilter.php @@ -30,6 +30,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Filter/TextFilter.php b/src/Filter/TextFilter.php index 053e6747f4..28249e417d 100644 --- a/src/Filter/TextFilter.php +++ b/src/Filter/TextFilter.php @@ -29,6 +29,7 @@ public static function new(string $propertyName, $label = null): self public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void { + // TODO : if filter property is compound, traverse the main property to get to the good one $alias = $filterDataDto->getEntityAlias(); $property = $filterDataDto->getProperty(); $comparison = $filterDataDto->getComparison(); diff --git a/src/Form/Type/FiltersFormType.php b/src/Form/Type/FiltersFormType.php index 46c9c53f7e..0436726ae8 100644 --- a/src/Form/Type/FiltersFormType.php +++ b/src/Form/Type/FiltersFormType.php @@ -20,7 +20,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) { /** @var FilterDto $filter */ foreach ($options['ea_filters'] as $filter) { - $builder->add($filter->getProperty(), $filter->getFormType(), $filter->getFormTypeOptions()); + $builder->add($filter->getName(), $filter->getFormType(), $filter->getFormTypeOptions()); } }