Skip to content

Commit

Permalink
feature #2794 Add support for unmapped filter fields (yceruto)
Browse files Browse the repository at this point in the history
This PR was merged into the 2.0.x-dev branch.

Discussion
----------

Add support for unmapped filter fields

The current exception is good for DX (typos via the YAML config), but we often need to create a filter that is not directly related to any mapped property.

Now users can achieve this by creating a custom Controller and overriding the `createFiltersForm ()` method, but this proposal also makes it possible through the YAML configuration:
```yaml
easy_admin:
    entities:
        Category:
            # ...
            list:
                filters: [{ property: 'country', type: 'App\Form\Filter\CountryFilterType', mapped: false }]
```
the new option `mapped` allow you to skip this EasyAdmin validation.

Closes #2793

Commits
-------

d9d82d6 Add support for unmapped filter fields
  • Loading branch information
javiereguiluz committed Jun 26, 2019
2 parents ca70237 + d9d82d6 commit 5d74854
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 17 deletions.
20 changes: 20 additions & 0 deletions doc/book/list-search-show-configuration.rst
Expand Up @@ -715,6 +715,26 @@ new filter to the field which will use it:
# optionally you can pass options to the filter class
# type_options: {}
.. tip::

When configuring filters to entities, all filters are validated. Any filter
properties that do not exist on the entity class will cause an exception to be
thrown.

In cases where you need extra filters in the form that will not be mapped to
the underlying entity class, you need to set the ``mapped`` option to ``false``::

# config/packages/easy_admin.yaml
easy_admin:
entities:
Users:
class: App\Entity\User
list:
filters:
- property: 'unmapped'
type: 'App\Form\Filter\CustomFilterType'
mapped: false

If the options passed to the filter are dynamic, you can't define them in the
YAML config file. Instead, :ref:`create a custom controller <overriding-the-entity-controller>`
for your entity and override the ``createFiltersForm()`` method::
Expand Down
32 changes: 16 additions & 16 deletions src/Configuration/PropertyConfigPass.php
Expand Up @@ -242,26 +242,26 @@ private function processFilterConfig(array $backendConfig): array
foreach ($entityConfig['list']['filters'] ?? [] as $propertyName => $filterConfig) {
$originalFilterConfig = $filterConfig;

if (!\array_key_exists($propertyName, $entityConfig['properties'])) {
throw new \InvalidArgumentException(\sprintf('The "%s" filter configured in the "list" view of the "%s" entity refers to a property called "%s" which is not defined in that entity.', $propertyName, $entityName, $propertyName));
}

// if the original filter didn't define the 'type' option, it will now
// be defined thanks to the 'type' value added by Doctrine's metadata
$filterConfig += $entityConfig['properties'][$propertyName];

if (!isset($originalFilterConfig['type'])) {
$guessedType = $this->filterRegistry->getTypeGuesser()
->guessType($entityConfig['class'], $propertyName);

if (null !== $guessedType) {
$filterConfig['type'] = $guessedType->getType();
$filterConfig['type_options'] = \array_replace_recursive($guessedType->getOptions(), $filterConfig['type_options']);
if (\array_key_exists($propertyName, $entityConfig['properties'])) {
// if the original filter didn't define the 'type' option, it will now
// be defined thanks to the 'type' value added by Doctrine's metadata
$filterConfig += $entityConfig['properties'][$propertyName];

if (!isset($originalFilterConfig['type'])) {
$guessedType = $this->filterRegistry->getTypeGuesser()
->guessType($entityConfig['class'], $propertyName);

if (null !== $guessedType) {
$filterConfig['type'] = $guessedType->getType();
$filterConfig['type_options'] = \array_replace_recursive($guessedType->getOptions(), $filterConfig['type_options']);
}
}
} elseif ($filterConfig['mapped'] ?? true) {
throw new \InvalidArgumentException(\sprintf('The "%s" filter configured in the "list" view of the "%s" entity refers to a property called "%s" which is not defined in that entity. Set the "mapped" option to false if it is not intended to be a mapped property.', $propertyName, $entityName, $propertyName));
}

if (!isset($filterConfig['type'])) {
throw new \InvalidArgumentException(\sprintf('The "%s" filter defined in the "list" view of the "%s" entity must define its own "type" explicitly because EasyAdmin cannot autoconfigure it using the "%s" data type of the associated property.', $propertyName, $entityName, $filterConfig['type']));
throw new \InvalidArgumentException(\sprintf('The "%s" filter defined in the "list" view of the "%s" entity must define its own "type" explicitly because EasyAdmin cannot autoconfigure it.', $propertyName, $entityName));
}

$backendConfig['entities'][$entityName]['list']['filters'][$propertyName] = $filterConfig;
Expand Down
Expand Up @@ -4,7 +4,7 @@
# EXCEPTION
expected_exception:
class: \InvalidArgumentException
message_string: 'The "this-property-does-not-exist" filter configured in the "list" view of the "Category" entity refers to a property called "this-property-does-not-exist" which is not defined in that entity.'
message_string: 'The "this-property-does-not-exist" filter configured in the "list" view of the "Category" entity refers to a property called "this-property-does-not-exist" which is not defined in that entity. Set the "mapped" option to false if it is not intended to be a mapped property.'

# CONFIGURATION
easy_admin:
Expand Down
@@ -0,0 +1,15 @@
# TEST
# items in the 'filters' option must define the 'type' option when they are unmapped

# EXCEPTION
expected_exception:
class: \InvalidArgumentException
message_string: 'The "foo" filter defined in the "list" view of the "Category" entity must define its own "type" explicitly because EasyAdmin cannot autoconfigure it.'

# CONFIGURATION
easy_admin:
entities:
Category:
class: AppTestBundle\Entity\UnitTests\Category
list:
filters: [{ property: 'foo', mapped: false }]

0 comments on commit 5d74854

Please sign in to comment.