diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b5118e29b..34adc2011 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,6 +19,9 @@ jobs: continue-on-error: ${{ matrix.allowed-to-fail }} + env: + SYMFONY_REQUIRE: ${{matrix.symfony-require}} + strategy: matrix: php-version: @@ -27,6 +30,7 @@ jobs: - '8.0' dependencies: [highest] allowed-to-fail: [false] + symfony-require: [""] variant: [normal] include: - php-version: '7.3' @@ -36,6 +40,7 @@ jobs: - php-version: '8.0' dependencies: highest allowed-to-fail: false + symfony-require: 4.4.* variant: 'symfony/symfony:"4.4.*"' - php-version: '8.0' dependencies: highest @@ -57,8 +62,12 @@ jobs: - name: Add PHPUnit matcher run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + - name: "Globally install symfony/flex" + if: matrix.symfony-require != '' + run: "composer global require --no-progress --no-scripts --no-plugins symfony/flex" + - name: Install variant - if: matrix.variant != 'normal' + if: matrix.variant != 'normal' && !startsWith(matrix.variant, 'symfony/symfony') run: composer require ${{ matrix.variant }} --no-update - name: "Install Composer dependencies (${{ matrix.dependencies }})" diff --git a/CHANGELOG.md b/CHANGELOG.md index d2b006e3f..408874a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [3.33.0](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/compare/3.32.1...3.33.0) - 2021-04-19 +### Added +- [[#1416](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1416)] "force_case_insensitivity" option to `StringFilter` in order to force the database to ignore the case sensitivity when matching filters. ([@phansys](https://github.com/phansys)) + +### Changed +- [[#1395](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1395)] Default value for the "case_sensitive" option from `true` to `null` in `StringFilter`. ([@phansys](https://github.com/phansys)) + +### Deprecated +- [[#1416](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1416)] "case_sensitive" option in `StringFilter`. ([@phansys](https://github.com/phansys)) + +### Fixed +- [[#1408](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1408)] Allow to decorate EntityManager ([@michkinn](https://github.com/michkinn)) +- [[#1414](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1414)] Return type for `ModelManager::getModelIdentifier()`. ([@phansys](https://github.com/phansys)) +- [[#1399](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1399)] Fixed triggering always deprecation when calling `ModelManager::getDefaultSortValues()` method ([@franmomu](https://github.com/franmomu)) + ## [3.32.1](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/compare/3.32.0...3.32.1) - 2021-04-06 ### Fixed - [[#1393](https://github.com/sonata-project/SonataDoctrineORMAdminBundle/pull/1393)] Added missing filter declaration in the config ([@VincentLanglet](https://github.com/VincentLanglet)) diff --git a/UPGRADE-3.x.md b/UPGRADE-3.x.md index 3bf55da41..36cc89e1d 100644 --- a/UPGRADE-3.x.md +++ b/UPGRADE-3.x.md @@ -4,6 +4,15 @@ UPGRADE 3.x UPGRADE FROM 3.x to 3.x ======================= +### Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter + +Not adding `Sonata\AdminBundle\Filter\Model\FilterData` as type declaration of argument 4 of the callable passed to +`Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter` is deprecated. In version 4.0 this argument will be an instance +of `Sonata\AdminBundle\Filter\Model\FilterData`. + +UPGRADE FROM 3.32 to 3.33 +========================= + ### Sonata\DoctrineORMAdminBundle\Filter\StringFilter The option "case_sensitive" is deprecated in favor of "force_case_insensitivity". diff --git a/composer.json b/composer.json index e3a1c9fbf..08073e924 100644 --- a/composer.json +++ b/composer.json @@ -26,10 +26,11 @@ ], "require": { "php": "^7.3 || ^8.0", + "doctrine/dbal": "^2.13", "doctrine/doctrine-bundle": "^2.3", - "doctrine/orm": "^2.5", - "doctrine/persistence": "^2.0", - "sonata-project/admin-bundle": "^4.0@alpha", + "doctrine/orm": "^2.8", + "doctrine/persistence": "^2.1", + "sonata-project/admin-bundle": "^4.0@dev", "sonata-project/exporter": "^2.0", "sonata-project/form-extensions": "^1.4", "symfony/config": "^4.4 || ^5.2", @@ -47,6 +48,7 @@ }, "conflict": { "simplethings/entity-audit-bundle": ">=2.0", + "sonata-project/admin-bundle": "<=4.0.0-alpha-1", "sonata-project/block-bundle": "<4.2" }, "provide": { @@ -67,7 +69,8 @@ "symfony/phpunit-bridge": "^5.2", "symfony/templating": "^4.4 || ^5.2", "symfony/yaml": "^4.4 || ^5.2", - "vimeo/psalm": "^4.1.1" + "vimeo/psalm": "^4.1.1", + "weirdan/doctrine-psalm-plugin": "^1.0" }, "suggest": { "sonata-project/entity-audit-bundle": "If you want to support for versioning of entities and their associations." diff --git a/docs/index.rst b/docs/index.rst index 8168a032c..a7ecdd02e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -18,6 +18,7 @@ The ``Doctrine ORM Admin`` provides services to work with the ``Admin Bundle`` a reference/templates reference/audit reference/query_proxy + reference/data_source reference/troubleshootings .. toctree:: diff --git a/docs/reference/data_source.rst b/docs/reference/data_source.rst new file mode 100644 index 000000000..1d7763f9e --- /dev/null +++ b/docs/reference/data_source.rst @@ -0,0 +1,52 @@ +.. index:: + double: Reference; Export / DataSource + +Export / DataSource +=================== + +When using an admins export feature you might want to modify how dates and times are exported. +This is done by calling ``setDateTimeFormat`` on the data source iterator. + +Here's one way to do it: + +1. Decorate the default Sonata\DoctrineORMAdminBundle\Exporter\DataSource with your own and call ``setDateTimeFormat`` there.:: + + dataSource = $dataSource; + } + + public function createIterator(ProxyQueryInterface $query, array $fields): SourceIteratorInterface + { + /** @var DoctrineORMQuerySourceIterator $iterator */ + $iterator = $this->dataSource->createIterator($query, $fields); + + $iterator->setDateTimeFormat('Y-m-d H:i:s'); + + return $iterator; + } + } + + +2. Add the your service in the ``config/services.yaml`` definition.:: + + services: + ... + App\Service\Admin\DecoratingDataSource: + decorates: 'sonata.admin.data_source.orm' + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 262923370..b335c16ad 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3,9 +3,6 @@ parameters: - # https://github.com/phpstan/phpstan-phpunit/issues/87 message: '#^Trying to mock an undefined method [a-zA-Z]*\(\) on class stdClass\.$#' path: tests/ - - # https://github.com/phpstan/phpstan/issues/4650 & https://github.com/phpstan/phpstan-src/pull/476 - message: '#^Strict comparison using === between array\(\) and array\&nonEmpty will always evaluate to false.$#' - path: src/Model/ModelManager.php - # https://github.com/doctrine/persistence/pull/163 message: '#Parameter \#1 \$class of method Sonata\\DoctrineORMAdminBundle\\FieldDescription\\FieldDescriptionFactory\:\:getMetadata\(\) expects class-string, string given.$#' path: src/FieldDescription/FieldDescriptionFactory.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 741e80f5c..c97193f66 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,8 +1,18 @@ - - - [CollectionType::class, 'Sonata\CoreBundle\Form\Type\CollectionType'] - + + + + [] === $idx + + + + + + static::$groupedOrExpressions + + + static::$groupedOrExpressions + diff --git a/psalm.xml b/psalm.xml index 9a5fdf075..2e9cc205e 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,5 +1,5 @@ - + @@ -10,5 +10,6 @@ + diff --git a/src/Builder/DatagridBuilder.php b/src/Builder/DatagridBuilder.php index 2f9193828..7e698223c 100644 --- a/src/Builder/DatagridBuilder.php +++ b/src/Builder/DatagridBuilder.php @@ -29,6 +29,9 @@ use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\FormFactoryInterface; +/** + * @phpstan-implements DatagridBuilderInterface + */ final class DatagridBuilder implements DatagridBuilderInterface { /** @@ -115,6 +118,7 @@ public function addFilter(DatagridInterface $datagrid, ?string $type, FieldDescr )); } + /** @phpstan-var class-string $type */ $type = $guessType->getType(); $fieldDescription->setType($type); @@ -147,7 +151,12 @@ public function getBaseDatagrid(AdminInterface $admin, array $values = []): Data $formBuilder = $this->formFactory->createNamedBuilder('filter', FormType::class, [], $defaultOptions); - return new Datagrid($admin->createQuery(), $admin->getList(), $pager, $formBuilder, $values); + $query = $admin->createQuery(); + if (!$query instanceof ProxyQueryInterface) { + throw new \TypeError(sprintf('The admin query MUST implement %s.', ProxyQueryInterface::class)); + } + + return new Datagrid($query, $admin->getList(), $pager, $formBuilder, $values); } /** diff --git a/src/Datagrid/Pager.php b/src/Datagrid/Pager.php index e0e5e9e2c..eea6cdc91 100644 --- a/src/Datagrid/Pager.php +++ b/src/Datagrid/Pager.php @@ -13,7 +13,6 @@ namespace Sonata\DoctrineORMAdminBundle\Datagrid; -use Doctrine\ORM\Tools\Pagination\Paginator; use Sonata\AdminBundle\Datagrid\Pager as BasePager; /** @@ -33,25 +32,11 @@ final class Pager extends BasePager public function getCurrentPageResults(): iterable { $query = $this->getQuery(); - if (!$query instanceof ProxyQueryInterface) { - throw new \TypeError(sprintf( - 'The pager query MUST implement %s.', - ProxyQueryInterface::class, - )); + if (null === $query) { + throw new \LogicException('The pager need a query to display results'); } - $identifierFieldNames = $query - ->getQueryBuilder() - ->getEntityManager() - ->getMetadataFactory() - ->getMetadataFor(current($query->getQueryBuilder()->getRootEntities())) - ->getIdentifierFieldNames(); - - // Paginator with fetchJoinCollection doesn't work with composite primary keys - // https://github.com/doctrine/orm/issues/2910 - $paginator = new Paginator($query->getDoctrineQuery(), 1 === \count($identifierFieldNames)); - - return $paginator->getIterator(); + return $query->execute(); } public function countResults(): int @@ -61,13 +46,13 @@ public function countResults(): int public function init(): void { - $this->resultsCount = $this->computeResultsCount(); - $query = $this->getQuery(); if (null === $query) { throw new \LogicException('The pager need a query to be initialised'); } + $this->resultsCount = \count($query->execute()); + $query->setFirstResult(null); $query->setMaxResults(null); @@ -82,17 +67,4 @@ public function init(): void $query->setMaxResults($this->getMaxPerPage()); } } - - private function computeResultsCount(): int - { - $query = $this->getQuery(); - - if (!$query instanceof ProxyQueryInterface) { - throw new \TypeError(sprintf('The pager query MUST implement %s.', ProxyQueryInterface::class)); - } - - $paginator = new Paginator($query->getDoctrineQuery()); - - return \count($paginator); - } } diff --git a/src/Datagrid/ProxyQuery.php b/src/Datagrid/ProxyQuery.php index c263e6795..b24d843d5 100644 --- a/src/Datagrid/ProxyQuery.php +++ b/src/Datagrid/ProxyQuery.php @@ -18,6 +18,7 @@ use Doctrine\ORM\EntityManager; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Tools\Pagination\Paginator; use Sonata\AdminBundle\Datagrid\ProxyQueryInterface as BaseProxyQueryInterface; /** @@ -137,6 +138,9 @@ public function __clone() $this->queryBuilder = clone $this->queryBuilder; } + /** + * @return Paginator + */ public function execute() { $query = $this->getDoctrineQuery(); @@ -145,7 +149,16 @@ public function execute() $query->setHint($name, $value); } - return $query->execute(); + $identifierFieldNames = $this + ->getQueryBuilder() + ->getEntityManager() + ->getMetadataFactory() + ->getMetadataFor(current($this->getQueryBuilder()->getRootEntities())) + ->getIdentifierFieldNames(); + + // Paginator with fetchJoinCollection doesn't work with composite primary keys + // https://github.com/doctrine/orm/issues/2910 + return new Paginator($query, 1 === \count($identifierFieldNames)); } /** diff --git a/src/DependencyInjection/Compiler/AddAuditEntityCompilerPass.php b/src/DependencyInjection/Compiler/AddAuditEntityCompilerPass.php index 803310142..d514c8392 100755 --- a/src/DependencyInjection/Compiler/AddAuditEntityCompilerPass.php +++ b/src/DependencyInjection/Compiler/AddAuditEntityCompilerPass.php @@ -17,6 +17,8 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; /** + * @internal + * * @author Thomas Rabaix */ final class AddAuditEntityCompilerPass implements CompilerPassInterface @@ -28,7 +30,9 @@ public function process(ContainerBuilder $container): void } $auditedEntities = $container->getParameter('simplethings.entityaudit.audited_entities'); + \assert(\is_array($auditedEntities)); $force = $container->getParameter('sonata_doctrine_orm_admin.audit.force'); + \assert(\is_bool($force)); foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $attributes) { if ('orm' !== $attributes[0]['manager_type']) { diff --git a/src/DependencyInjection/Compiler/AddGuesserCompilerPass.php b/src/DependencyInjection/Compiler/AddGuesserCompilerPass.php index 0af1d74e9..ee5161c0f 100644 --- a/src/DependencyInjection/Compiler/AddGuesserCompilerPass.php +++ b/src/DependencyInjection/Compiler/AddGuesserCompilerPass.php @@ -18,6 +18,8 @@ use Symfony\Component\DependencyInjection\Reference; /** + * @internal + * * @author Thomas Rabaix */ final class AddGuesserCompilerPass implements CompilerPassInterface diff --git a/src/DependencyInjection/Compiler/AddTemplatesCompilerPass.php b/src/DependencyInjection/Compiler/AddTemplatesCompilerPass.php index d3a4398c2..5f134e3b5 100644 --- a/src/DependencyInjection/Compiler/AddTemplatesCompilerPass.php +++ b/src/DependencyInjection/Compiler/AddTemplatesCompilerPass.php @@ -18,17 +18,14 @@ use Symfony\Component\DependencyInjection\Definition; /** + * @internal + * * @author Thomas Rabaix */ final class AddTemplatesCompilerPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void { - $overwrite = $container->getParameter('sonata.admin.configuration.admin_services'); - \assert(\is_array($overwrite)); - $templates = $container->getParameter('sonata_doctrine_orm_admin.templates'); - \assert(\is_array($templates)); - foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $attributes) { if (!isset($attributes[0]['manager_type']) || 'orm' !== $attributes[0]['manager_type']) { continue; @@ -36,29 +33,22 @@ public function process(ContainerBuilder $container): void $definition = $container->getDefinition($id); - if (!$definition->hasMethodCall('setFormTheme')) { - $definition->addMethodCall('setFormTheme', [$templates['form']]); - } - - if (isset($overwrite[$id]['templates']['form'])) { - $this->mergeMethodCall($definition, 'setFormTheme', $overwrite[$id]['templates']['form']); - } - - if (!$definition->hasMethodCall('setFilterTheme')) { - $definition->addMethodCall('setFilterTheme', [$templates['filter']]); - } - - if (isset($overwrite[$id]['templates']['filter'])) { - $this->mergeMethodCall($definition, 'setFilterTheme', $overwrite[$id]['templates']['filter']); - } + $this->mergeMethodCall($definition, 'setFormTheme', ['@SonataDoctrineORMAdmin/Form/form_admin_fields.html.twig']); + $this->mergeMethodCall($definition, 'setFilterTheme', ['@SonataDoctrineORMAdmin/Form/filter_admin_fields.html.twig']); } } /** - * @param mixed $value + * @param array $value */ public function mergeMethodCall(Definition $definition, string $name, $value): void { + if (!$definition->hasMethodCall($name)) { + $definition->addMethodCall($name, [$value]); + + return; + } + $methodCalls = $definition->getMethodCalls(); foreach ($methodCalls as &$calls) { diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 482332475..06dfa2e03 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\DependencyInjection; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -32,9 +33,10 @@ final class Configuration implements ConfigurationInterface public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('sonata_doctrine_orm_admin'); + $rootNode = $treeBuilder->getRootNode(); + \assert($rootNode instanceof ArrayNodeDefinition); - $treeBuilder - ->getRootNode() + $rootNode ->children() ->scalarNode('entity_manager')->defaultNull()->end() ->arrayNode('audit') @@ -46,14 +48,6 @@ public function getConfigTreeBuilder(): TreeBuilder ->arrayNode('templates') ->addDefaultsIfNotSet() ->children() - ->arrayNode('form') - ->prototype('scalar')->end() - ->defaultValue(['@SonataDoctrineORMAdmin/Form/form_admin_fields.html.twig']) - ->end() - ->arrayNode('filter') - ->prototype('scalar')->end() - ->defaultValue(['@SonataDoctrineORMAdmin/Form/filter_admin_fields.html.twig']) - ->end() ->arrayNode('types') ->children() ->arrayNode('list') diff --git a/src/Filter/AbstractDateFilter.php b/src/Filter/AbstractDateFilter.php index 5f1290f5c..5ef92b0a7 100644 --- a/src/Filter/AbstractDateFilter.php +++ b/src/Filter/AbstractDateFilter.php @@ -13,6 +13,8 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Doctrine\DBAL\Types\Types; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DateRangeType; use Sonata\AdminBundle\Form\Type\Filter\DateTimeRangeType; use Sonata\AdminBundle\Form\Type\Filter\DateTimeType; @@ -45,105 +47,55 @@ abstract class AbstractDateFilter extends Filter */ protected $time = false; - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { // check data sanity - if (!\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } if ($this->range) { - // additional data check for ranged items - if (!\array_key_exists('start', $data['value']) || !\array_key_exists('end', $data['value'])) { - return; - } + $this->filterRange($query, $alias, $field, $data); - if (!$data['value']['start'] && !$data['value']['end']) { - return; - } + return; + } - // date filter should filter records for the whole days - if (false === $this->time && ($data['value']['end'] instanceof \DateTime || $data['value']['end'] instanceof \DateTimeImmutable)) { - // since the received `\DateTime` object uses the model timezone to represent - // the value submitted by the view (which can use a different timezone) and this - // value is intended to contain a time in the begining of a date (IE, if the model - // object is configured to use UTC timezone, the view object "2020-11-07 00:00:00.0-03:00" - // is transformed to "2020-11-07 03:00:00.0+00:00" in the model object), we increment - // the time part by adding "23:59:59" in order to cover the whole end date and get proper - // results from queries like "o.created_at <= :date_end". - $data['value']['end'] = $data['value']['end']->modify('+23 hours 59 minutes 59 seconds'); - } + $value = $data->getValue(); + if (!$value instanceof \DateTimeInterface) { + return; + } + \assert($value instanceof \DateTime || $value instanceof \DateTimeImmutable); - // transform types - if ('timestamp' === $this->getOption('input_type')) { - $data['value']['start'] = $data['value']['start'] instanceof \DateTimeInterface ? $data['value']['start']->getTimestamp() : 0; - $data['value']['end'] = $data['value']['end'] instanceof \DateTimeInterface ? $data['value']['end']->getTimestamp() : 0; - } + // default type for simple filter + $type = $data->getType() ?? DateOperatorType::TYPE_EQUAL; - // default type for range filter - $data['type'] = !isset($data['type']) || !is_numeric($data['type']) ? DateRangeOperatorType::TYPE_BETWEEN : $data['type']; + // date filter should filter records for the whole day + if (false === $this->time && DateOperatorType::TYPE_EQUAL === $type) { + // the value comparison will be made with the '>=' operator + $type = DateOperatorType::TYPE_GREATER_EQUAL; - $startDateParameterName = $this->getNewParameterName($query); $endDateParameterName = $this->getNewParameterName($query); + $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '<', $endDateParameterName)); - if (DateRangeOperatorType::TYPE_NOT_BETWEEN === $data['type']) { - $this->applyWhere($query, sprintf('%s.%s < :%s OR %s.%s > :%s', $alias, $field, $startDateParameterName, $alias, $field, $endDateParameterName)); - } else { - if ($data['value']['start']) { - $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '>=', $startDateParameterName)); - } - - if ($data['value']['end']) { - $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '<=', $endDateParameterName)); - } - } - - if ($data['value']['start']) { - $query->getQueryBuilder()->setParameter($startDateParameterName, $data['value']['start']); - } - - if ($data['value']['end']) { - $query->getQueryBuilder()->setParameter($endDateParameterName, $data['value']['end']); - } - } else { - if (!$data['value']) { - return; - } - - // default type for simple filter - $data['type'] = $data['type'] ?? DateOperatorType::TYPE_EQUAL; - - // just find an operator and apply query - $operator = $this->getOperator($data['type']); - - // transform types if ('timestamp' === $this->getOption('input_type')) { - $data['value'] = $data['value'] instanceof \DateTimeInterface ? $data['value']->getTimestamp() : 0; + $endValue = strtotime('+1 day', $value->getTimestamp()); + } else { + $endValue = clone $value; + $endValue->add(new \DateInterval('P1D')); } - $parameterName = $this->getNewParameterName($query); - - // date filter should filter records for the whole day - if (false === $this->time && DateOperatorType::TYPE_EQUAL === $data['type']) { - $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '>=', $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($endDateParameterName, $endValue, $this->getParameterType($endValue)); + } - $endDateParameterName = $this->getNewParameterName($query); - $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '<', $endDateParameterName)); - if ('timestamp' === $this->getOption('input_type')) { - $endValue = strtotime('+1 day', $data['value']); - } else { - $endValue = clone $data['value']; - $endValue->add(new \DateInterval('P1D')); - } - $query->getQueryBuilder()->setParameter($endDateParameterName, $endValue); + // just find an operator and apply query + $operator = $this->getOperator($type); - return; - } + // transform types + $value = 'timestamp' === $this->getOption('input_type') ? $value->getTimestamp() : $value; - $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); - } + $parameterName = $this->getNewParameterName($query); + $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName)); + $query->getQueryBuilder()->setParameter($parameterName, $value, $this->getParameterType($value)); } public function getDefaultOptions(): array @@ -172,6 +124,97 @@ public function getRenderSettings(): array ]]; } + public function filterRange(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void + { + $value = $data->getValue(); + + // additional data check for ranged items + if ( + !\is_array($value) + || !\array_key_exists('start', $value) + || !\array_key_exists('end', $value) + ) { + return; + } + + if ( + !$value['start'] instanceof \DateTimeInterface + && !$value['end'] instanceof \DateTimeInterface + ) { + return; + } + + // date filter should filter records for the whole days + if ( + false === $this->time + && ($value['end'] instanceof \DateTime || $value['end'] instanceof \DateTimeImmutable) + ) { + // since the received `\DateTime` object uses the model timezone to represent + // the value submitted by the view (which can use a different timezone) and this + // value is intended to contain a time in the begining of a date (IE, if the model + // object is configured to use UTC timezone, the view object "2020-11-07 00:00:00.0-03:00" + // is transformed to "2020-11-07 03:00:00.0+00:00" in the model object), we increment + // the time part by adding "23:59:59" in order to cover the whole end date and get proper + // results from queries like "o.created_at <= :date_end". + $value['end'] = $value['end']->modify('+23 hours 59 minutes 59 seconds'); + } + + // transform types + if ('timestamp' === $this->getOption('input_type')) { + $value['start'] = $value['start'] instanceof \DateTimeInterface ? $value['start']->getTimestamp() : 0; + $value['end'] = $value['end'] instanceof \DateTimeInterface ? $value['end']->getTimestamp() : 0; + } + + // default type for range filter + $type = $data->getType() ?? DateRangeOperatorType::TYPE_BETWEEN; + + $startDateParameterName = $this->getNewParameterName($query); + $endDateParameterName = $this->getNewParameterName($query); + + if (DateRangeOperatorType::TYPE_NOT_BETWEEN === $type) { + $this->applyWhere($query, sprintf('%s.%s < :%s OR %s.%s > :%s', $alias, $field, $startDateParameterName, $alias, $field, $endDateParameterName)); + } else { + if ($value['start']) { + $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '>=', $startDateParameterName)); + } + + if ($value['end']) { + $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, '<=', $endDateParameterName)); + } + } + + if ($value['start']) { + $query->getQueryBuilder()->setParameter( + $startDateParameterName, + $value['start'], + $this->getParameterType($value['start']) + ); + } + + if ($value['end']) { + $query->getQueryBuilder()->setParameter( + $endDateParameterName, + $value['end'], + $this->getParameterType($value['end']) + ); + } + } + + /** + * @param \DateTime|\DateTimeImmutable|int $parameter + */ + private function getParameterType($parameter): string + { + if ($parameter instanceof \DateTime) { + return Types::DATETIME_MUTABLE; + } + if ($parameter instanceof \DateTimeImmutable) { + return Types::DATETIME_IMMUTABLE; + } + + return Types::INTEGER; + } + private function getOperator(int $type): string { if (!isset(self::CHOICES[$type])) { diff --git a/src/Filter/BooleanFilter.php b/src/Filter/BooleanFilter.php index b6dea9817..4db5f4faa 100644 --- a/src/Filter/BooleanFilter.php +++ b/src/Filter/BooleanFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Sonata\Form\Type\BooleanType; @@ -20,15 +21,17 @@ final class BooleanFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('type', $data) || !\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - if (\is_array($data['value'])) { + $value = $data->getValue(); + + if (\is_array($value)) { $values = []; - foreach ($data['value'] as $v) { + foreach ($value as $v) { if (!\in_array($v, [BooleanType::TYPE_NO, BooleanType::TYPE_YES], true)) { continue; } @@ -42,13 +45,13 @@ public function filter(ProxyQueryInterface $query, string $alias, string $field, $this->applyWhere($query, $query->getQueryBuilder()->expr()->in(sprintf('%s.%s', $alias, $field), $values)); } else { - if (!\in_array($data['value'], [BooleanType::TYPE_NO, BooleanType::TYPE_YES], true)) { + if (!\in_array($value, [BooleanType::TYPE_NO, BooleanType::TYPE_YES], true)) { return; } $parameterName = $this->getNewParameterName($query); $this->applyWhere($query, sprintf('%s.%s = :%s', $alias, $field, $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, (BooleanType::TYPE_YES === $data['value']) ? 1 : 0); + $query->getQueryBuilder()->setParameter($parameterName, (BooleanType::TYPE_YES === $value) ? 1 : 0); } } diff --git a/src/Filter/CallbackFilter.php b/src/Filter/CallbackFilter.php index 704f20163..b7796a7bd 100644 --- a/src/Filter/CallbackFilter.php +++ b/src/Filter/CallbackFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -20,7 +21,7 @@ final class CallbackFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { if (!\is_callable($this->getOption('callback'))) { throw new \RuntimeException(sprintf('Please provide a valid callback option "filter" for field "%s"', $this->getName())); @@ -58,7 +59,7 @@ public function getRenderSettings(): array ]]; } - protected function association(ProxyQueryInterface $query, array $data): array + protected function association(ProxyQueryInterface $query, FilterData $data): array { $alias = $query->entityJoin($this->getParentAssociationMappings()); diff --git a/src/Filter/ChoiceFilter.php b/src/Filter/ChoiceFilter.php index 244b45227..b1c51e7d1 100644 --- a/src/Filter/ChoiceFilter.php +++ b/src/Filter/ChoiceFilter.php @@ -13,19 +13,20 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; final class ChoiceFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('type', $data) || !\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - if (\is_array($data['value'])) { + if (\is_array($data->getValue())) { $this->filterWithMultipleValues($query, $alias, $field, $data); } else { $this->filterWithSingleValue($query, $alias, $field, $data); @@ -51,23 +52,18 @@ public function getRenderSettings(): array ]]; } - /** - * @param mixed[] $data - * - * @phpstan-param array{type: int|null, value: mixed} $data - */ - private function filterWithMultipleValues(ProxyQueryInterface $query, string $alias, string $field, array $data): void + private function filterWithMultipleValues(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (0 === \count($data['value'])) { + if (0 === \count($data->getValue())) { return; } - $isNullSelected = \in_array(null, $data['value'], true); + $isNullSelected = \in_array(null, $data->getValue(), true); $completeField = sprintf('%s.%s', $alias, $field); $parameterName = $this->getNewParameterName($query); $or = $query->getQueryBuilder()->expr()->orX(); - if (EqualOperatorType::TYPE_NOT_EQUAL === $data['type']) { + if ($data->isType(EqualOperatorType::TYPE_NOT_EQUAL)) { $or->add($query->getQueryBuilder()->expr()->notIn($completeField, ':'.$parameterName)); if (!$isNullSelected) { @@ -82,39 +78,34 @@ private function filterWithMultipleValues(ProxyQueryInterface $query, string $al } $this->applyWhere($query, $or); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } - /** - * @param mixed[] $data - * - * @phpstan-param array{type: int|null, value: mixed} $data - */ - private function filterWithSingleValue(ProxyQueryInterface $query, string $alias, string $field, array $data): void + private function filterWithSingleValue(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if ('' === $data['value'] || false === $data['value']) { + if ('' === $data->getValue() || false === $data->getValue()) { return; } $parameterName = $this->getNewParameterName($query); $completeField = sprintf('%s.%s', $alias, $field); - if (EqualOperatorType::TYPE_NOT_EQUAL === $data['type']) { - if (null === $data['value']) { + if ($data->isType(EqualOperatorType::TYPE_NOT_EQUAL)) { + if (null === $data->getValue()) { $this->applyWhere($query, $query->getQueryBuilder()->expr()->isNotNull($completeField)); } else { $this->applyWhere( $query, sprintf('%s != :%s OR %s IS NULL', $completeField, $parameterName, $completeField) ); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } } else { - if (null === $data['value']) { + if (null === $data->getValue()) { $this->applyWhere($query, $query->getQueryBuilder()->expr()->isNull($completeField)); } else { $this->applyWhere($query, sprintf('%s = :%s', $completeField, $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } } } diff --git a/src/Filter/ClassFilter.php b/src/Filter/ClassFilter.php index e2f5c8c5e..739b36103 100644 --- a/src/Filter/ClassFilter.php +++ b/src/Filter/ClassFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; @@ -25,20 +26,20 @@ final class ClassFilter extends Filter EqualOperatorType::TYPE_NOT_EQUAL => 'NOT INSTANCE OF', ]; - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - if (0 === \strlen($data['value'])) { + if (0 === \strlen($data->getValue())) { return; } - $type = $data['type'] ?? EqualOperatorType::TYPE_EQUAL; + $type = $data->getType() ?? EqualOperatorType::TYPE_EQUAL; $operator = $this->getOperator($type); - $this->applyWhere($query, sprintf('%s %s %s', $alias, $operator, $data['value'])); + $this->applyWhere($query, sprintf('%s %s %s', $alias, $operator, $data->getValue())); } public function getDefaultOptions(): array diff --git a/src/Filter/CountFilter.php b/src/Filter/CountFilter.php index f5d8a94b3..609366a97 100644 --- a/src/Filter/CountFilter.php +++ b/src/Filter/CountFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\NumberType; use Sonata\AdminBundle\Form\Type\Operator\NumberOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; @@ -28,13 +29,13 @@ final class CountFilter extends Filter NumberOperatorType::TYPE_LESS_THAN => '<', ]; - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data) || !is_numeric($data['value'])) { + if (!$data->hasValue() || !is_numeric($data->getValue())) { return; } - $type = $data['type'] ?? NumberOperatorType::TYPE_EQUAL; + $type = $data->getType() ?? NumberOperatorType::TYPE_EQUAL; $operator = $this->getOperator($type); // c.name > '1' => c.name OPERATOR :FIELDNAME @@ -42,7 +43,7 @@ public function filter(ProxyQueryInterface $query, string $alias, string $field, $rootAlias = current($query->getQueryBuilder()->getRootAliases()); $query->getQueryBuilder()->addGroupBy($rootAlias); $this->applyHaving($query, sprintf('COUNT(%s.%s) %s :%s', $alias, $field, $operator, $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } public function getDefaultOptions(): array diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php index dd57325c9..69a221433 100644 --- a/src/Filter/Filter.php +++ b/src/Filter/Filter.php @@ -16,6 +16,7 @@ use Doctrine\ORM\Query\Expr\Orx; use Sonata\AdminBundle\Datagrid\ProxyQueryInterface as BaseProxyQueryInterface; use Sonata\AdminBundle\Filter\Filter as BaseFilter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; abstract class Filter extends BaseFilter @@ -35,20 +36,16 @@ abstract class Filter extends BaseFilter /** * Apply the filter to the QueryBuilder instance. - * - * @param mixed[] $data - * - * @phpstan-param array{type?: int|null, value?: mixed} $data */ - abstract public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void; + abstract public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void; - public function apply(BaseProxyQueryInterface $query, array $filterData): void + public function apply(BaseProxyQueryInterface $query, FilterData $filterData): void { if (!$query instanceof ProxyQueryInterface) { throw new \TypeError(sprintf('The query MUST implement %s.', ProxyQueryInterface::class)); } - if (\array_key_exists('value', $filterData)) { + if ($filterData->hasValue()) { [$alias, $field] = $this->association($query, $filterData); $this->filter($query, $alias, $field, $filterData); @@ -61,14 +58,11 @@ public function isActive(): bool } /** - * @param mixed[] $data - * * @return string[] * - * @phpstan-param array{type?: int|null, value?: mixed} $data * @phpstan-return array{string, string} */ - protected function association(ProxyQueryInterface $query, array $data): array + protected function association(ProxyQueryInterface $query, FilterData $data): array { $alias = $query->entityJoin($this->getParentAssociationMappings()); diff --git a/src/Filter/ModelAutocompleteFilter.php b/src/Filter/ModelAutocompleteFilter.php index a8e9c4a1d..ecc16ecf4 100644 --- a/src/Filter/ModelAutocompleteFilter.php +++ b/src/Filter/ModelAutocompleteFilter.php @@ -14,6 +14,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; use Doctrine\Common\Collections\Collection; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\AdminBundle\Form\Type\ModelAutocompleteType; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; @@ -21,17 +22,19 @@ final class ModelAutocompleteFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - if ($data['value'] instanceof Collection) { - $data['value'] = $data['value']->toArray(); + $value = $data->getValue(); + + if ($value instanceof Collection) { + $data = $data->changeValue($value->toArray()); } - if (\is_array($data['value'])) { + if (\is_array($data->getValue())) { $this->handleMultiple($query, $alias, $data); } else { $this->handleModel($query, $alias, $data); @@ -63,47 +66,42 @@ public function getRenderSettings(): array /** * For the record, the $alias value is provided by the association method (and the entity join method) * so the field value is not used here. - * - * @param mixed[] $data */ - protected function handleMultiple(ProxyQueryInterface $query, string $alias, array $data): void + protected function handleMultiple(ProxyQueryInterface $query, string $alias, FilterData $data): void { - if (0 === \count($data['value'])) { + if (0 === \count($data->getValue())) { return; } $parameterName = $this->getNewParameterName($query); - if (isset($data['type']) && EqualOperatorType::TYPE_NOT_EQUAL === $data['type']) { + if ($data->isType(EqualOperatorType::TYPE_NOT_EQUAL)) { $this->applyWhere($query, $query->getQueryBuilder()->expr()->notIn($alias, ':'.$parameterName)); } else { $this->applyWhere($query, $query->getQueryBuilder()->expr()->in($alias, ':'.$parameterName)); } - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } - /** - * @param mixed[] $data - */ - protected function handleModel(ProxyQueryInterface $query, string $alias, array $data): void + protected function handleModel(ProxyQueryInterface $query, string $alias, FilterData $data): void { - if (empty($data['value'])) { + if (empty($data->getValue())) { return; } $parameterName = $this->getNewParameterName($query); - if (isset($data['type']) && EqualOperatorType::TYPE_NOT_EQUAL === $data['type']) { + if ($data->isType(EqualOperatorType::TYPE_NOT_EQUAL)) { $this->applyWhere($query, sprintf('%s != :%s', $alias, $parameterName)); } else { $this->applyWhere($query, sprintf('%s = :%s', $alias, $parameterName)); } - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } - protected function association(ProxyQueryInterface $query, array $data): array + protected function association(ProxyQueryInterface $query, FilterData $data): array { $associationMappings = $this->getParentAssociationMappings(); $associationMappings[] = $this->getAssociationMapping(); diff --git a/src/Filter/ModelFilter.php b/src/Filter/ModelFilter.php index 4ae8ffcfe..d702e945c 100644 --- a/src/Filter/ModelFilter.php +++ b/src/Filter/ModelFilter.php @@ -15,6 +15,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping\ClassMetadata; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; @@ -22,18 +23,18 @@ final class ModelFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data) || empty($data['value'])) { + if (!$data->hasValue() || empty($data->getValue())) { return; } - if ($data['value'] instanceof Collection) { - $data['value'] = $data['value']->toArray(); - } + $value = $data->getValue(); - if (!\is_array($data['value'])) { - $data['value'] = [$data['value']]; + if ($value instanceof Collection) { + $data = $data->changeValue($value->toArray()); + } elseif (!\is_array($value)) { + $data = $data->changeValue([$value]); } $this->handleMultiple($query, $alias, $data); @@ -65,18 +66,16 @@ public function getRenderSettings(): array /** * For the record, the $alias value is provided by the association method (and the entity join method) * so the field value is not used here. - * - * @param mixed[] $data */ - protected function handleMultiple(ProxyQueryInterface $query, string $alias, array $data): void + protected function handleMultiple(ProxyQueryInterface $query, string $alias, FilterData $data): void { - if (0 === \count($data['value'])) { + if (0 === \count($data->getValue())) { return; } $parameterName = $this->getNewParameterName($query); - if (isset($data['type']) && EqualOperatorType::TYPE_NOT_EQUAL === $data['type']) { + if ($data->isType(EqualOperatorType::TYPE_NOT_EQUAL)) { $or = $query->getQueryBuilder()->expr()->orX(); $or->add($query->getQueryBuilder()->expr()->notIn($alias, ':'.$parameterName)); @@ -96,10 +95,10 @@ protected function handleMultiple(ProxyQueryInterface $query, string $alias, arr $this->applyWhere($query, $query->getQueryBuilder()->expr()->in($alias, ':'.$parameterName)); } - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } - protected function association(ProxyQueryInterface $query, array $data): array + protected function association(ProxyQueryInterface $query, FilterData $data): array { $types = [ ClassMetadata::ONE_TO_ONE, diff --git a/src/Filter/NullFilter.php b/src/Filter/NullFilter.php index fba291af9..82755ceed 100644 --- a/src/Filter/NullFilter.php +++ b/src/Filter/NullFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\DefaultType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Sonata\Form\Type\BooleanType; @@ -20,14 +21,14 @@ final class NullFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - $isYes = BooleanType::TYPE_YES === (int) $data['value']; - $isNo = BooleanType::TYPE_NO === (int) $data['value']; + $isYes = BooleanType::TYPE_YES === (int) $data->getValue(); + $isNo = BooleanType::TYPE_NO === (int) $data->getValue(); if (!$this->getOption('inverse') && $isYes || $this->getOption('inverse') && $isNo) { $this->applyWhere( diff --git a/src/Filter/NumberFilter.php b/src/Filter/NumberFilter.php index 2ab6b30bb..c2700aecb 100644 --- a/src/Filter/NumberFilter.php +++ b/src/Filter/NumberFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\NumberType; use Sonata\AdminBundle\Form\Type\Operator\NumberOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; @@ -28,19 +29,19 @@ final class NumberFilter extends Filter NumberOperatorType::TYPE_LESS_THAN => '<', ]; - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data) || !is_numeric($data['value'])) { + if (!$data->hasValue() || !is_numeric($data->getValue())) { return; } - $type = $data['type'] ?? NumberOperatorType::TYPE_EQUAL; + $type = $data->getType() ?? NumberOperatorType::TYPE_EQUAL; $operator = $this->getOperator($type); // c.name > '1' => c.name OPERATOR :FIELDNAME $parameterName = $this->getNewParameterName($query); $this->applyWhere($query, sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName)); - $query->getQueryBuilder()->setParameter($parameterName, $data['value']); + $query->getQueryBuilder()->setParameter($parameterName, $data->getValue()); } public function getDefaultOptions(): array diff --git a/src/Filter/StringFilter.php b/src/Filter/StringFilter.php index 9240907aa..e5219feeb 100644 --- a/src/Filter/StringFilter.php +++ b/src/Filter/StringFilter.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\ChoiceType; use Sonata\AdminBundle\Form\Type\Operator\StringOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; @@ -43,17 +44,17 @@ final class StringFilter extends Filter StringOperatorType::TYPE_NOT_CONTAINS, ]; - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - $data['value'] = $this->trim((string) ($data['value'] ?? '')); - $type = $data['type'] ?? StringOperatorType::TYPE_CONTAINS; + $value = $this->trim((string) ($data->getValue() ?? '')); + $type = $data->getType() ?? StringOperatorType::TYPE_CONTAINS; // ignore empty value if it doesn't make sense - if ('' === $data['value'] && + if ('' === $value && (!$this->getOption('allow_empty') || \in_array($type, self::MEANINGLESS_TYPES, true)) ) { return; @@ -64,9 +65,9 @@ public function filter(ProxyQueryInterface $query, string $alias, string $field, // c.name > '1' => c.name OPERATOR :FIELDNAME $parameterName = $this->getNewParameterName($query); - $forceCaseInsensitivity = true === $this->getOption('force_case_insensitivity', false); + $forceCaseInsensitivity = $this->getOption('force_case_insensitivity', false); - if ($forceCaseInsensitivity && '' !== $data['value']) { + if ($forceCaseInsensitivity && '' !== $value) { $clause = 'LOWER(%s.%s) %s :%s'; } else { $clause = '%s.%s %s :%s'; @@ -101,7 +102,7 @@ public function filter(ProxyQueryInterface $query, string $alias, string $field, $parameterName, sprintf( $format, - true !== $forceCaseInsensitivity || '' === $data['value'] ? $data['value'] : mb_strtolower($data['value']) + $forceCaseInsensitivity && '' !== $value ? mb_strtolower($value) : $value ) ); } diff --git a/src/Filter/StringListFilter.php b/src/Filter/StringListFilter.php index 53e541041..21b9cbe74 100644 --- a/src/Filter/StringListFilter.php +++ b/src/Filter/StringListFilter.php @@ -13,34 +13,37 @@ namespace Sonata\DoctrineORMAdminBundle\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Filter\ChoiceType; use Sonata\AdminBundle\Form\Type\Operator\ContainsOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; final class StringListFilter extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { - if (!\array_key_exists('type', $data) || !\array_key_exists('value', $data)) { + if (!$data->hasValue()) { return; } - if (!\is_array($data['value'])) { - $data['value'] = [$data['value']]; + $value = $data->getValue(); + + if (!\is_array($value)) { + $data = $data->changeValue([$value]); } - $operator = ContainsOperatorType::TYPE_NOT_CONTAINS === $data['type'] ? 'NOT LIKE' : 'LIKE'; + $operator = $data->isType(ContainsOperatorType::TYPE_NOT_CONTAINS) ? 'NOT LIKE' : 'LIKE'; $andConditions = $query->getQueryBuilder()->expr()->andX(); - foreach ($data['value'] as $item) { + foreach ($data->getValue() as $item) { $parameterName = $this->getNewParameterName($query); $andConditions->add(sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName)); $query->getQueryBuilder()->setParameter($parameterName, '%'.serialize($item).'%'); } - if (ContainsOperatorType::TYPE_EQUAL === $data['type']) { - $andConditions->add(sprintf("%s.%s LIKE 'a:%s:%%'", $alias, $field, \count($data['value']))); + if ($data->isType(ContainsOperatorType::TYPE_EQUAL)) { + $andConditions->add(sprintf("%s.%s LIKE 'a:%s:%%'", $alias, $field, \count($data->getValue()))); } $this->applyWhere($query, $andConditions); diff --git a/src/Model/ModelManager.php b/src/Model/ModelManager.php index 402379fb5..918d2e052 100644 --- a/src/Model/ModelManager.php +++ b/src/Model/ModelManager.php @@ -14,7 +14,7 @@ namespace Sonata\DoctrineORMAdminBundle\Model; use Doctrine\Common\Util\ClassUtils; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\LockMode; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; @@ -34,6 +34,11 @@ use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +/** + * @phpstan-template T of object + * @phpstan-implements ModelManagerInterface + * @phpstan-implements LockInterface + */ final class ModelManager implements ModelManagerInterface, LockInterface { public const ID_SEPARATOR = '~'; @@ -65,16 +70,10 @@ public function create(object $object): void $entityManager = $this->getEntityManager($object); $entityManager->persist($object); $entityManager->flush(); - } catch (\PDOException $e) { - throw new ModelManagerException( - sprintf('Failed to create object: %s', ClassUtils::getClass($object)), - $e->getCode(), - $e - ); - } catch (DBALException $e) { + } catch (\PDOException | Exception $e) { throw new ModelManagerException( sprintf('Failed to create object: %s', ClassUtils::getClass($object)), - $e->getCode(), + (int) $e->getCode(), $e ); } @@ -86,16 +85,10 @@ public function update(object $object): void $entityManager = $this->getEntityManager($object); $entityManager->persist($object); $entityManager->flush(); - } catch (\PDOException $e) { - throw new ModelManagerException( - sprintf('Failed to update object: %s', ClassUtils::getClass($object)), - $e->getCode(), - $e - ); - } catch (DBALException $e) { + } catch (\PDOException | Exception $e) { throw new ModelManagerException( sprintf('Failed to update object: %s', ClassUtils::getClass($object)), - $e->getCode(), + (int) $e->getCode(), $e ); } @@ -107,16 +100,10 @@ public function delete(object $object): void $entityManager = $this->getEntityManager($object); $entityManager->remove($object); $entityManager->flush(); - } catch (\PDOException $e) { - throw new ModelManagerException( - sprintf('Failed to delete object: %s', ClassUtils::getClass($object)), - $e->getCode(), - $e - ); - } catch (DBALException $e) { + } catch (\PDOException | Exception $e) { throw new ModelManagerException( sprintf('Failed to delete object: %s', ClassUtils::getClass($object)), - $e->getCode(), + (int) $e->getCode(), $e ); } @@ -145,10 +132,14 @@ public function lock(object $object, ?int $expectedVersion): void $entityManager = $this->getEntityManager($object); $entityManager->lock($object, LockMode::OPTIMISTIC, $expectedVersion); } catch (OptimisticLockException $e) { - throw new LockException($e->getMessage(), $e->getCode(), $e); + throw new LockException($e->getMessage(), (int) $e->getCode(), $e); } } + /** + * @phpstan-param class-string $class + * @phpstan-return T|null + */ public function find(string $class, $id): ?object { $values = array_combine($this->getIdentifierFieldNames($class), explode(self::ID_SEPARATOR, (string) $id)); @@ -156,11 +147,19 @@ public function find(string $class, $id): ?object return $this->getEntityManager($class)->getRepository($class)->find($values); } + /** + * @phpstan-param class-string $class + * @phpstan-return array + */ public function findBy(string $class, array $criteria = []): array { return $this->getEntityManager($class)->getRepository($class)->findBy($criteria); } + /** + * @phpstan-param class-string $class + * @phpstan-return T|null + */ public function findOneBy(string $class, array $criteria = []): ?object { return $this->getEntityManager($class)->getRepository($class)->findOneBy($criteria); @@ -210,7 +209,10 @@ public function executeQuery(object $query) } if ($query instanceof ProxyQuery) { - return $query->execute(); + /** @phpstan-var \Doctrine\ORM\Tools\Pagination\Paginator $results */ + $results = $query->execute(); + + return $results; } throw new \InvalidArgumentException(sprintf( @@ -292,8 +294,6 @@ public function getUrlSafeIdentifier(object $entity): ?string } /** - * @phpstan-param non-empty-array $idx - * * @throws \InvalidArgumentException if value passed as argument 3 is an empty array */ public function addIdentifiersToQuery(string $class, BaseProxyQueryInterface $query, array $idx): void @@ -343,7 +343,7 @@ public function batchDelete(string $class, BaseProxyQueryInterface $query): void $entityManager = $this->getEntityManager($class); $i = 0; - foreach ($qb->getQuery()->iterate() as $pos => $object) { + foreach ($qb->getQuery()->toIterable() as $pos => $object) { $entityManager->remove($object[0]); if (0 === (++$i % 20)) { @@ -354,8 +354,12 @@ public function batchDelete(string $class, BaseProxyQueryInterface $query): void $entityManager->flush(); $entityManager->clear(); - } catch (\PDOException | DBALException $e) { - throw new ModelManagerException('', 0, $e); + } catch (\PDOException | Exception $e) { + throw new ModelManagerException( + sprintf('Failed to delete object: %s', $class), + (int) $e->getCode(), + $e + ); } } diff --git a/src/SonataDoctrineORMAdminBundle.php b/src/SonataDoctrineORMAdminBundle.php index 425525b28..1fb2cc2b5 100644 --- a/src/SonataDoctrineORMAdminBundle.php +++ b/src/SonataDoctrineORMAdminBundle.php @@ -16,6 +16,7 @@ use Sonata\DoctrineORMAdminBundle\DependencyInjection\Compiler\AddAuditEntityCompilerPass; use Sonata\DoctrineORMAdminBundle\DependencyInjection\Compiler\AddGuesserCompilerPass; use Sonata\DoctrineORMAdminBundle\DependencyInjection\Compiler\AddTemplatesCompilerPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -24,7 +25,7 @@ final class SonataDoctrineORMAdminBundle extends Bundle public function build(ContainerBuilder $container): void { $container->addCompilerPass(new AddGuesserCompilerPass()); - $container->addCompilerPass(new AddTemplatesCompilerPass()); + $container->addCompilerPass(new AddTemplatesCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -1); $container->addCompilerPass(new AddAuditEntityCompilerPass()); } } diff --git a/src/Util/ObjectAclManipulator.php b/src/Util/ObjectAclManipulator.php index 466083123..bfbce41b8 100644 --- a/src/Util/ObjectAclManipulator.php +++ b/src/Util/ObjectAclManipulator.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Util; +use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManager; use Doctrine\Persistence\ManagerRegistry; use Sonata\AdminBundle\Admin\AdminInterface; @@ -47,11 +48,12 @@ public function batchConfigureAcls(OutputInterface $output, AdminInterface $admi $output->writeln(sprintf(' > generate ACLs for %s', $admin->getCode())); $objectOwnersMsg = null === $securityIdentity ? '' : ' and set the object owner'; - $em = $this->registry->getManagerForClass($admin->getClass()); + $class = $admin->getClass(); + $em = $this->registry->getManagerForClass($class); \assert($em instanceof EntityManager); $qb = $em->createQueryBuilder(); - $qb->select('o')->from($admin->getClass(), 'o'); + $qb->select('o')->from($class, 'o'); $count = 0; $countUpdated = 0; @@ -62,12 +64,9 @@ public function batchConfigureAcls(OutputInterface $output, AdminInterface $admi $batchSizeOutput = 200; $objectIds = []; - foreach ($qb->getQuery()->iterate() as $row) { + foreach ($qb->getQuery()->toIterable() as $row) { $objectIds[] = ObjectIdentity::fromDomainObject($row[0]); - // detach from Doctrine, so that it can be Garbage-Collected immediately - $em->detach($row[0]); - ++$count; if (0 === ($count % $batchSize)) { @@ -87,8 +86,12 @@ public function batchConfigureAcls(OutputInterface $output, AdminInterface $admi $countAdded += $batchAdded; $countUpdated += $batchUpdated; } - } catch (\PDOException $e) { - throw new ModelManagerException('', 0, $e); + } catch (\PDOException | Exception $e) { + throw new ModelManagerException( + sprintf('Failed to configure acl for class: %s', $class), + (int) $e->getCode(), + $e + ); } $output->writeln(sprintf( diff --git a/tests/App/Admin/AuthorAdmin.php b/tests/App/Admin/AuthorAdmin.php index 25d54afc3..a02fad69b 100644 --- a/tests/App/Admin/AuthorAdmin.php +++ b/tests/App/Admin/AuthorAdmin.php @@ -26,7 +26,7 @@ /** * @phpstan-extends AbstractAdmin<\Sonata\DoctrineORMAdminBundle\Tests\App\Entity\Author> */ -final class AuthorAdmin extends AbstractAdmin +class AuthorAdmin extends AbstractAdmin { protected function configureListFields(ListMapper $list): void { diff --git a/tests/App/Admin/AuthorWithSimplePagerAdmin.php b/tests/App/Admin/AuthorWithSimplePagerAdmin.php new file mode 100644 index 000000000..928e5a03a --- /dev/null +++ b/tests/App/Admin/AuthorWithSimplePagerAdmin.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\DoctrineORMAdminBundle\Tests\App\Admin; + +final class AuthorWithSimplePagerAdmin extends AuthorAdmin +{ + protected $baseRoutePattern = 'author-with-simple-pager'; + protected $baseRouteName = 'author_with_simple_pager'; +} diff --git a/tests/App/DataFixtures/BookFixtures.php b/tests/App/DataFixtures/BookFixtures.php index b3f5a647a..bf03f2307 100644 --- a/tests/App/DataFixtures/BookFixtures.php +++ b/tests/App/DataFixtures/BookFixtures.php @@ -58,7 +58,7 @@ public function load(ObjectManager $manager): void } /** - * @phpstan-return class-string[] + * @phpstan-return array> */ public function getDependencies(): array { diff --git a/tests/App/DataFixtures/ItemFixtures.php b/tests/App/DataFixtures/ItemFixtures.php index 752022d22..046e14191 100644 --- a/tests/App/DataFixtures/ItemFixtures.php +++ b/tests/App/DataFixtures/ItemFixtures.php @@ -44,7 +44,7 @@ public function load(ObjectManager $manager): void } /** - * @phpstan-return class-string[] + * @phpstan-return array> */ public function getDependencies(): array { diff --git a/tests/App/config/services.php b/tests/App/config/services.php index 2b0feca7d..4bc0d9b83 100644 --- a/tests/App/config/services.php +++ b/tests/App/config/services.php @@ -11,7 +11,9 @@ * file that was distributed with this source code. */ +use Sonata\AdminBundle\Datagrid\Pager; use Sonata\DoctrineORMAdminBundle\Tests\App\Admin\AuthorAdmin; +use Sonata\DoctrineORMAdminBundle\Tests\App\Admin\AuthorWithSimplePagerAdmin; use Sonata\DoctrineORMAdminBundle\Tests\App\Admin\BookAdmin; use Sonata\DoctrineORMAdminBundle\Tests\App\Admin\CarAdmin; use Sonata\DoctrineORMAdminBundle\Tests\App\Admin\CategoryAdmin; @@ -58,6 +60,20 @@ ->tag('sonata.admin', [ 'manager_type' => 'orm', 'label' => 'Author', + 'default' => true, + ]) + ->args([ + '', + Author::class, + null, + ]) + ->call('setTemplate', ['outer_list_rows_list', 'author/list_outer_list_rows_list.html.twig']) + + ->set(AuthorWithSimplePagerAdmin::class) + ->tag('sonata.admin', [ + 'manager_type' => 'orm', + 'label' => 'Author with Simple Pager', + 'pager_type' => Pager::TYPE_SIMPLE, ]) ->args([ '', diff --git a/tests/Builder/DatagridBuilderTest.php b/tests/Builder/DatagridBuilderTest.php index f7775d6cc..ee769cad4 100644 --- a/tests/Builder/DatagridBuilderTest.php +++ b/tests/Builder/DatagridBuilderTest.php @@ -21,13 +21,13 @@ use Sonata\AdminBundle\Datagrid\Datagrid; use Sonata\AdminBundle\Datagrid\DatagridInterface; use Sonata\AdminBundle\Datagrid\Pager; -use Sonata\AdminBundle\Datagrid\ProxyQueryInterface; use Sonata\AdminBundle\Datagrid\SimplePager; use Sonata\AdminBundle\FieldDescription\FieldDescriptionCollection; use Sonata\AdminBundle\FieldDescription\TypeGuesserInterface; use Sonata\AdminBundle\Filter\FilterFactoryInterface; use Sonata\AdminBundle\Translator\FormLabelTranslatorStrategy; use Sonata\DoctrineORMAdminBundle\Builder\DatagridBuilder; +use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Sonata\DoctrineORMAdminBundle\FieldDescription\FieldDescription; use Sonata\DoctrineORMAdminBundle\Filter\ModelAutocompleteFilter; use Symfony\Component\Form\FormBuilderInterface; @@ -151,7 +151,7 @@ public function testFixFieldDescriptionWithoutFieldName(): void public function testAddFilterNoType(): void { - $datagrid = $this->createStub(DatagridInterface::class); + $datagrid = $this->createMock(DatagridInterface::class); $guessType = $this->createStub(TypeGuess::class); $fieldDescription = new FieldDescription('test'); diff --git a/tests/Datagrid/ProxyQueryTest.php b/tests/Datagrid/ProxyQueryTest.php index 8519a53d6..4786ba76b 100644 --- a/tests/Datagrid/ProxyQueryTest.php +++ b/tests/Datagrid/ProxyQueryTest.php @@ -72,9 +72,7 @@ public function testSetHint(): void $this->em->persist($entity2); $this->em->flush(); - $qb = $this->em->createQueryBuilder() - ->select('o.id') - ->from(DoubleNameEntity::class, 'o'); + $qb = $this->em->createQueryBuilder()->select('o')->from(DoubleNameEntity::class, 'o'); $pq = new ProxyQuery($qb); $pq->setHint( @@ -83,9 +81,9 @@ public function testSetHint(): void ); $pq->setHint('hint', 'value'); - $result = $pq->execute(); + $result = iterator_to_array($pq->execute()); - $this->assertSame(2, $result[0]['id']); + $this->assertSame([$entity2, $entity1], $result); } public function testSortOrderValidatesItsInput(): void @@ -135,31 +133,17 @@ public function testExecuteWithOrderBy(): void $this->em->flush(); $query = new ProxyQuery( - $this->em->createQueryBuilder()->select('o.id')->from(DoubleNameEntity::class, 'o') + $this->em->createQueryBuilder()->select('o')->from(DoubleNameEntity::class, 'o') ); $query->setSortBy([], ['fieldName' => 'name2'])->setSortOrder('ASC'); - $this->assertSame( - [ - ['id' => 1], - ['id' => 2], - ['id' => 3], - ], - $query->execute() - ); + $this->assertSame([$entity1, $entity2, $entity3], iterator_to_array($query->execute())); $query2 = new ProxyQuery( - $this->em->createQueryBuilder()->select('o.id')->from(DoubleNameEntity::class, 'o')->addOrderBy('o.name') + $this->em->createQueryBuilder()->select('o')->from(DoubleNameEntity::class, 'o')->addOrderBy('o.name') ); $query2->setSortBy([], ['fieldName' => 'name2'])->setSortOrder('ASC'); - $this->assertSame( - [ - ['id' => 2], - ['id' => 1], - ['id' => 3], - ], - $query2->execute() - ); + $this->assertSame([$entity2, $entity1, $entity3], iterator_to_array($query2->execute())); } } diff --git a/tests/DependencyInjection/Compiler/AddGuesserCompilerPassTest.php b/tests/DependencyInjection/Compiler/AddGuesserCompilerPassTest.php index 4294235aa..c19c6694a 100755 --- a/tests/DependencyInjection/Compiler/AddGuesserCompilerPassTest.php +++ b/tests/DependencyInjection/Compiler/AddGuesserCompilerPassTest.php @@ -23,7 +23,7 @@ class AddGuesserCompilerPassTest extends TestCase { public function testProcess(): void { - $containerBuilder = $this->createStub(ContainerBuilder::class); + $containerBuilder = $this->createMock(ContainerBuilder::class); $definition = $this->createMock(Definition::class); $definition->expects($this->exactly(3))->method('replaceArgument')->with(0, [new Reference('some.id')]); diff --git a/tests/DependencyInjection/Compiler/AddTemplatesCompilerPassTest.php b/tests/DependencyInjection/Compiler/AddTemplatesCompilerPassTest.php index 431ea7fd2..ab394366f 100644 --- a/tests/DependencyInjection/Compiler/AddTemplatesCompilerPassTest.php +++ b/tests/DependencyInjection/Compiler/AddTemplatesCompilerPassTest.php @@ -25,37 +25,14 @@ public function testDefaultBehavior(): void $container = $this->createMock(ContainerBuilder::class); $container - ->expects($this->any()) - ->method('getParameter') - ->willReturnCallback(static function ($value) { - if ('sonata.admin.configuration.admin_services' === $value) { - return [ - 'my.admin' => [ - 'templates' => [ - 'form' => ['myform.twig.html'], - 'filter' => ['myfilter.twig.html'], - ], - ], - ]; - } - - if ('sonata_doctrine_orm_admin.templates' === $value) { - return [ - 'form' => ['default_form.twig.html'], - 'filter' => ['default_filter.twig.html'], - ]; - } - }); - - $container - ->expects($this->any()) + ->expects($this->once()) ->method('findTaggedServiceIds') ->willReturn(['my.admin' => [['manager_type' => 'orm']]]); $definition = new Definition(null); $container - ->expects($this->any()) + ->expects($this->once()) ->method('getDefinition') ->willReturn($definition); @@ -65,8 +42,8 @@ public function testDefaultBehavior(): void $compilerPass->process($container); $expected = [ - ['setFilterTheme', [['custom_call.twig.html', 'myfilter.twig.html']]], - ['setFormTheme', [['default_form.twig.html', 'myform.twig.html']]], + ['setFilterTheme', [['custom_call.twig.html', '@SonataDoctrineORMAdmin/Form/filter_admin_fields.html.twig']]], + ['setFormTheme', [['@SonataDoctrineORMAdmin/Form/form_admin_fields.html.twig']]], ]; $this->assertSame($expected, $definition->getMethodCalls()); diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index 0f4093596..4e967e8c7 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -27,14 +27,6 @@ public function testDefaultOptions(): void $this->assertNull($config['entity_manager']); $this->assertTrue($config['audit']['force']); - $this->assertContains( - '@SonataDoctrineORMAdmin/Form/form_admin_fields.html.twig', - $config['templates']['form'] - ); - $this->assertContains( - '@SonataDoctrineORMAdmin/Form/filter_admin_fields.html.twig', - $config['templates']['filter'] - ); $this->assertArrayNotHasKey('types', $config['templates']); } @@ -53,8 +45,6 @@ public function testCustomTemplates(): void { $config = $this->process([[ 'templates' => [ - 'form' => ['form.twig.html', 'form_extra.twig.html'], - 'filter' => ['filter.twig.html'], 'types' => [ 'list' => [ 'array' => 'list_array.twig.html', @@ -66,8 +56,6 @@ public function testCustomTemplates(): void ], ]]); - $this->assertSame(['form.twig.html', 'form_extra.twig.html'], $config['templates']['form']); - $this->assertSame(['filter.twig.html'], $config['templates']['filter']); $this->assertSame([ 'list' => [ 'array' => 'list_array.twig.html', diff --git a/tests/FieldDescription/FieldDescriptionTest.php b/tests/FieldDescription/FieldDescriptionTest.php index d7eb795c6..aa4ccda96 100644 --- a/tests/FieldDescription/FieldDescriptionTest.php +++ b/tests/FieldDescription/FieldDescriptionTest.php @@ -66,8 +66,6 @@ public function testOptions(): void $expected = [ 'misc' => 'foobar', - 'placeholder' => 'short_object_description_placeholder', - 'link_parameters' => [], 'array' => [ 'key1' => 'key_1', 'key2' => 'key_2', diff --git a/tests/Filter/BooleanFilterTest.php b/tests/Filter/BooleanFilterTest.php index 7ec3430b2..3b5595108 100644 --- a/tests/Filter/BooleanFilterTest.php +++ b/tests/Filter/BooleanFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\BooleanFilter; use Sonata\Form\Type\BooleanType; @@ -37,8 +38,8 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); - $filter->filter($proxyQuery, 'alias', 'field', [null, 'test']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([null, 'test'])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -51,7 +52,7 @@ public function testFilterNo(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => null, 'value' => BooleanType::TYPE_NO]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => BooleanType::TYPE_NO])); $this->assertSameQuery(['WHERE alias.field = :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => 0], $proxyQuery); @@ -65,7 +66,7 @@ public function testFilterYes(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => null, 'value' => BooleanType::TYPE_YES]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => BooleanType::TYPE_YES])); $this->assertSameQuery(['WHERE alias.field = :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => 1], $proxyQuery); @@ -79,7 +80,7 @@ public function testFilterArray(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => null, 'value' => [BooleanType::TYPE_NO]]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => [BooleanType::TYPE_NO]])); $this->assertSameQuery(['WHERE alias.field IN ("0")'], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/CallbackFilterTest.php b/tests/Filter/CallbackFilterTest.php index a292610b4..c32287282 100644 --- a/tests/Filter/CallbackFilterTest.php +++ b/tests/Filter/CallbackFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter; @@ -39,15 +40,15 @@ public function testFilterClosure(): void $filter = new CallbackFilter(); $filter->initialize('field_name', [ - 'callback' => static function (ProxyQueryInterface $query, string $alias, string $field, array $data): bool { + 'callback' => static function (ProxyQueryInterface $query, string $alias, string $field, FilterData $data): bool { $query->getQueryBuilder()->andWhere(sprintf('CUSTOM QUERY %s.%s', $alias, $field)); - $query->getQueryBuilder()->setParameter('value', $data['value']); + $query->getQueryBuilder()->setParameter('value', $data->getValue()); return true; }, ]); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'myValue']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'myValue'])); $this->assertSameQuery(['WHERE CUSTOM QUERY alias.field'], $proxyQuery); $this->assertSameQueryParameters(['value' => 'myValue'], $proxyQuery); @@ -63,20 +64,17 @@ public function testFilterMethod(): void 'callback' => [$this, 'customCallback'], ]); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'myValue']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'myValue'])); $this->assertSameQuery(['WHERE CUSTOM QUERY alias.field'], $proxyQuery); $this->assertSameQueryParameters(['value' => 'myValue'], $proxyQuery); $this->assertTrue($filter->isActive()); } - /** - * @phpstan-param array{value: mixed} $data - */ - public function customCallback(ProxyQueryInterface $query, string $alias, string $field, array $data): bool + public function customCallback(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): bool { $query->getQueryBuilder()->andWhere(sprintf('CUSTOM QUERY %s.%s', $alias, $field)); - $query->getQueryBuilder()->setParameter('value', $data['value']); + $query->getQueryBuilder()->setParameter('value', $data->getValue()); return true; } @@ -89,7 +87,7 @@ public function testFilterException(): void $filter->initialize('field_name', []); $this->expectException(\RuntimeException::class); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'myValue']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'myValue'])); } public function testApplyMethod(): void @@ -98,16 +96,16 @@ public function testApplyMethod(): void $filter = new CallbackFilter(); $filter->initialize('field_name_test', [ - 'callback' => static function (ProxyQueryInterface $query, string $alias, string $field, array $data): bool { + 'callback' => static function (ProxyQueryInterface $query, string $alias, string $field, FilterData $data): bool { $query->getQueryBuilder()->andWhere(sprintf('CUSTOM QUERY %s.%s', $alias, $field)); - $query->getQueryBuilder()->setParameter('value', $data['value']); + $query->getQueryBuilder()->setParameter('value', $data->getValue()); return true; }, 'field_name' => 'field_name_test', ]); - $filter->apply($proxyQuery, ['value' => 'myValue']); + $filter->apply($proxyQuery, FilterData::fromArray(['value' => 'myValue'])); $this->assertSameQuery(['WHERE CUSTOM QUERY o.field_name_test'], $proxyQuery); $this->assertSameQueryParameters(['value' => 'myValue'], $proxyQuery); diff --git a/tests/Filter/ChoiceFilterTest.php b/tests/Filter/ChoiceFilterTest.php index f38c5673d..61e00ac28 100644 --- a/tests/Filter/ChoiceFilterTest.php +++ b/tests/Filter/ChoiceFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\ChoiceFilter; @@ -36,7 +37,7 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -49,7 +50,7 @@ public function testFilterArray(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', '2']]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', '2']])); $this->assertSameQuery(['WHERE alias.field IN :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => ['1', '2']], $proxyQuery); @@ -57,7 +58,7 @@ public function testFilterArray(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', '2']]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', '2']])); $this->assertSameQuery(['WHERE alias.field NOT IN :field_name_0 OR alias.field IS NULL'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => ['1', '2']], $proxyQuery); @@ -71,7 +72,7 @@ public function testFilterArrayWithNullValue(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', null]]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', null]])); $this->assertSameQuery(['WHERE alias.field IN :field_name_0 OR alias.field IS NULL'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => ['1', null]], $proxyQuery); @@ -79,7 +80,7 @@ public function testFilterArrayWithNullValue(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', null]]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', null]])); $this->assertSameQuery(['WHERE alias.field NOT IN :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => ['1', null]], $proxyQuery); @@ -93,7 +94,7 @@ public function testFilterScalar(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => '1']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => '1'])); $this->assertSameQuery(['WHERE alias.field = :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '1'], $proxyQuery); @@ -101,7 +102,7 @@ public function testFilterScalar(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => '1']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => '1'])); $this->assertSameQuery(['WHERE alias.field != :field_name_0 OR alias.field IS NULL'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '1'], $proxyQuery); @@ -115,7 +116,7 @@ public function testFilterNull(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => null]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => null])); $this->assertSameQuery(['WHERE alias.field IS NULL'], $proxyQuery); $this->assertSameQueryParameters([], $proxyQuery); @@ -123,7 +124,7 @@ public function testFilterNull(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => null]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => null])); $this->assertSameQuery(['WHERE alias.field IS NOT NULL'], $proxyQuery); $this->assertSameQueryParameters([], $proxyQuery); @@ -137,7 +138,7 @@ public function testFilterZero(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 0]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 0])); $this->assertSameQuery(['WHERE alias.field = :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => 0], $proxyQuery); diff --git a/tests/Filter/ClassFilterTest.php b/tests/Filter/ClassFilterTest.php index 96b93be50..03fe4509d 100644 --- a/tests/Filter/ClassFilterTest.php +++ b/tests/Filter/ClassFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\ClassFilter; @@ -36,7 +37,7 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => '']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => ''])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -49,7 +50,7 @@ public function testFilterInvalidOperator(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => 42]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => 42])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -62,9 +63,9 @@ public function testFilter(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'type']); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 'type']); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'type']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'type'])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 'type'])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'type'])); $expected = [ 'WHERE alias INSTANCE OF type', diff --git a/tests/Filter/CountFilterTest.php b/tests/Filter/CountFilterTest.php index 1d5ced91d..6f4cb171e 100644 --- a/tests/Filter/CountFilterTest.php +++ b/tests/Filter/CountFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\NumberOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\CountFilter; @@ -26,7 +27,7 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -39,7 +40,7 @@ public function testFilterInvalidOperator(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => 42]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => 42])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -55,7 +56,7 @@ public function testFilter(string $expected, ?int $type): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => $type, 'value' => 42]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => $type, 'value' => 42])); $this->assertSameQuery(['GROUP BY o', $expected], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/DateFilterTest.php b/tests/Filter/DateFilterTest.php index 4e2cbc7cb..3baf48c16 100644 --- a/tests/Filter/DateFilterTest.php +++ b/tests/Filter/DateFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\DateFilter; use Symfony\Component\Form\Extension\Core\Type\DateType; @@ -29,7 +30,7 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -46,11 +47,11 @@ public function testFilterRecordsWholeDay(): void $filter->initialize('field_name', ['field_options' => ['class' => 'FooBar']]); $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => new \DateTime()]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => new \DateTime()])); $this->assertSameQuery([ - 'WHERE alias.field >= :field_name_0', - 'WHERE alias.field < :field_name_1', + 'WHERE alias.field < :field_name_0', + 'WHERE alias.field >= :field_name_1', ], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/DateRangeFilterTest.php b/tests/Filter/DateRangeFilterTest.php index 3cc5dadb7..fa6dcf2a6 100644 --- a/tests/Filter/DateRangeFilterTest.php +++ b/tests/Filter/DateRangeFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\DateRangeFilter; @@ -28,17 +29,17 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); - $filter->filter($proxyQuery, 'alias', 'field', [null, 'test']); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => null, 'value' => []]); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([null, 'test'])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => []])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => ['start' => null, 'end' => null], - ]); - $filter->filter($proxyQuery, 'alias', 'field', [ + ])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => ['start' => '', 'end' => ''], - ]); + ])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -54,13 +55,13 @@ public function testFilterStartDateAndEndDate(): void $startDateTime = new \DateTime('2016-08-01'); $endDateTime = new \DateTime('2016-08-31'); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => [ 'start' => $startDateTime, 'end' => $endDateTime, ], - ]); + ])); $this->assertSameQuery(['WHERE alias.field >= :field_name_0', 'WHERE alias.field <= :field_name_1'], $proxyQuery); $this->assertSameQueryParameters([ @@ -79,13 +80,13 @@ public function testFilterStartDate(): void $startDateTime = new \DateTime('2016-08-01'); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => [ 'start' => $startDateTime, 'end' => '', ], - ]); + ])); $this->assertSameQuery(['WHERE alias.field >= :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => $startDateTime], $proxyQuery); @@ -101,13 +102,13 @@ public function testFilterEndDate(): void $endDateTime = new \DateTime('2016-08-31'); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => [ 'start' => '', 'end' => $endDateTime, ], - ]); + ])); $this->assertSameQuery(['WHERE alias.field <= :field_name_1'], $proxyQuery); $this->assertSameQueryParameters(['field_name_1' => $endDateTime], $proxyQuery); @@ -133,13 +134,13 @@ public function testFilterEndDateCoversWholeDay( $this->assertSame($modelTimeZone->getName(), $modelEndDateTime->getTimezone()->getName()); $this->assertNotSame($modelTimeZone->getName(), $viewEndDateTime->getTimezone()->getName()); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => [ 'start' => '', 'end' => $modelEndDateTime, ], - ]); + ])); $this->assertTrue($filter->isActive()); $this->assertSameQuery(['WHERE alias.field <= :field_name_1'], $proxyQuery); @@ -192,13 +193,13 @@ public function testFilterEndDateImmutable(): void $endDateTime = new \DateTimeImmutable('2016-08-31'); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => null, 'value' => [ 'start' => '', 'end' => $endDateTime, ], - ]); + ])); $this->assertSameQuery(['WHERE alias.field <= :field_name_1'], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/DateTimeFilterTest.php b/tests/Filter/DateTimeFilterTest.php index 89c1f4e72..a60a81c8c 100644 --- a/tests/Filter/DateTimeFilterTest.php +++ b/tests/Filter/DateTimeFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\DateTimeFilter; use Symfony\Component\Form\Extension\Core\Type\DateTimeType; @@ -29,7 +30,7 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => '']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => ''])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); diff --git a/tests/Filter/DateTimeRangeFilterTest.php b/tests/Filter/DateTimeRangeFilterTest.php index 224759cd9..42e99c6b9 100644 --- a/tests/Filter/DateTimeRangeFilterTest.php +++ b/tests/Filter/DateTimeRangeFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\DateTimeRangeFilter; use Sonata\Form\Type\DateTimeRangeType; @@ -29,7 +30,7 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); diff --git a/tests/Filter/FilterTest.php b/tests/Filter/FilterTest.php index 311edfede..a9ac6fac5 100644 --- a/tests/Filter/FilterTest.php +++ b/tests/Filter/FilterTest.php @@ -15,6 +15,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query\Expr; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQueryInterface; use Sonata\DoctrineORMAdminBundle\Filter\Filter; @@ -73,7 +74,7 @@ public function testOrExpression(string $expected, array $filterOptionsCollectio $proxyQuery = new ProxyQuery($queryBuilder); - foreach ($filterOptionsCollection as [$type, $orGroup, $defaultOptions, $field, $options]) { + foreach ($filterOptionsCollection as [$type, $orGroup, $defaultOptions, $field, $filterData]) { $filter = new $type(); $filter->initialize($field, $defaultOptions); $filter->setCondition(Filter::CONDITION_OR); @@ -81,7 +82,7 @@ public function testOrExpression(string $expected, array $filterOptionsCollectio $filter->setOption('or_group', $orGroup); } - $filter->apply($proxyQuery, $options); + $filter->apply($proxyQuery, $filterData); } // More custom conditions set after the filters. @@ -91,7 +92,7 @@ public function testOrExpression(string $expected, array $filterOptionsCollectio } /** - * @phpstan-return iterable, string, array}>}> + * @phpstan-return iterable, string, FilterData}>}> */ public function orExpressionProvider(): iterable { @@ -107,9 +108,9 @@ public function orExpressionProvider(): iterable 'allow_empty' => false, ], 'project', - [ + FilterData::fromArray([ 'value' => 'sonata-project', - ], + ]), ], [ StringFilter::class, @@ -119,9 +120,9 @@ public function orExpressionProvider(): iterable 'allow_empty' => false, ], 'version', - [ + FilterData::fromArray([ 'value' => '3.x', - ], + ]), ], ], ]; @@ -138,9 +139,9 @@ public function orExpressionProvider(): iterable 'allow_empty' => false, ], 'project', - [ + FilterData::fromArray([ 'value' => 'sonata-project', - ], + ]), ], ], ]; @@ -158,9 +159,9 @@ public function orExpressionProvider(): iterable 'allow_empty' => false, ], 'project', - [ + FilterData::fromArray([ 'value' => 'sonata-project', - ], + ]), ], [ StringFilter::class, @@ -170,9 +171,9 @@ public function orExpressionProvider(): iterable 'allow_empty' => false, ], 'version', - [ + FilterData::fromArray([ 'value' => '3.x', - ], + ]), ], ], ]; @@ -181,7 +182,7 @@ public function orExpressionProvider(): iterable private function createFilter(): Filter { return new class() extends Filter { - public function filter(ProxyQueryInterface $query, string $alias, string $field, array $data): void + public function filter(ProxyQueryInterface $query, string $alias, string $field, FilterData $data): void { // TODO: Implement filter() method. throw new \BadMethodCallException(sprintf( diff --git a/tests/Filter/ModelFilterTest.php b/tests/Filter/ModelFilterTest.php index 02b615cf9..d5de888f2 100644 --- a/tests/Filter/ModelFilterTest.php +++ b/tests/Filter/ModelFilterTest.php @@ -14,6 +14,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; use Doctrine\ORM\Mapping\ClassMetadata; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\EqualOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\ModelFilter; @@ -27,7 +28,7 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -40,10 +41,10 @@ public function testFilterArray(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', '2'], - ]); + ])); // the alias is now computer by the entityJoin method $this->assertSameQuery(['WHERE alias IN :field_name_0'], $proxyQuery); @@ -58,10 +59,10 @@ public function testFilterArrayTypeIsNotEqual(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', [ + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', '2'], - ]); + ])); // the alias is now computer by the entityJoin method $this->assertSameQuery( @@ -79,7 +80,7 @@ public function testFilterScalar(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 2]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 2])); $this->assertSameQuery(['WHERE alias IN :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => [2]], $proxyQuery); @@ -93,7 +94,7 @@ public function testFilterScalarTypeIsNotEqual(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 2]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 2])); $this->assertSameQuery( ['WHERE alias NOT IN :field_name_0 OR IDENTITY('.current(($proxyQuery->getRootAliases())).'.field_name) IS NULL'], @@ -106,14 +107,14 @@ public function testFilterScalarTypeIsNotEqual(): void public function testAssociationWithInvalidMapping(): void { - $this->expectException(\RuntimeException::class); - $filter = new ModelFilter(); $filter->initialize('field_name', ['mapping_type' => 'foo']); $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, ['value' => 'asd']); + $this->expectException(\RuntimeException::class); + + $filter->apply($proxyQuery, FilterData::fromArray(['value' => 'asd'])); } public function testAssociationWithValidMappingAndEmptyFieldName(): void @@ -125,7 +126,7 @@ public function testAssociationWithValidMappingAndEmptyFieldName(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, ['value' => 'asd']); + $filter->apply($proxyQuery, FilterData::fromArray(['value' => 'asd'])); $this->assertTrue($filter->isActive()); } @@ -142,7 +143,7 @@ public function testAssociationWithValidMapping(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd']); + $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd'])); $this->assertSameQuery([ 'LEFT JOIN o.association_mapping AS s_association_mapping', @@ -172,7 +173,7 @@ public function testAssociationWithValidParentAssociationMappings(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, ['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd']); + $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd'])); $this->assertSameQuery([ 'LEFT JOIN o.association_mapping AS s_association_mapping', diff --git a/tests/Filter/NullFilterTest.php b/tests/Filter/NullFilterTest.php index 49de7190b..335f4f386 100644 --- a/tests/Filter/NullFilterTest.php +++ b/tests/Filter/NullFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\NullFilter; use Sonata\Form\Type\BooleanType; @@ -28,7 +29,7 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -48,7 +49,7 @@ public function testValue(bool $inverse, int $value, string $expectedQuery): voi $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value])); $this->assertSameQuery([$expectedQuery], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/NumberFilterTest.php b/tests/Filter/NumberFilterTest.php index 3681ffa26..4d1e5daaa 100644 --- a/tests/Filter/NumberFilterTest.php +++ b/tests/Filter/NumberFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\NumberOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\NumberFilter; @@ -26,7 +27,7 @@ public function testFilterEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -39,7 +40,7 @@ public function testFilterInvalidOperator(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => 42]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => 42])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -52,12 +53,12 @@ public function testFilter(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => NumberOperatorType::TYPE_EQUAL, 'value' => 42]); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => NumberOperatorType::TYPE_GREATER_EQUAL, 'value' => 42]); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => NumberOperatorType::TYPE_GREATER_THAN, 'value' => 42]); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => NumberOperatorType::TYPE_LESS_EQUAL, 'value' => 42]); - $filter->filter($proxyQuery, 'alias', 'field', ['type' => NumberOperatorType::TYPE_LESS_THAN, 'value' => 42]); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 42]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => NumberOperatorType::TYPE_EQUAL, 'value' => 42])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => NumberOperatorType::TYPE_GREATER_EQUAL, 'value' => 42])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => NumberOperatorType::TYPE_GREATER_THAN, 'value' => 42])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => NumberOperatorType::TYPE_LESS_EQUAL, 'value' => 42])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => NumberOperatorType::TYPE_LESS_THAN, 'value' => 42])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 42])); $expected = [ 'WHERE alias.field = :field_name_0', diff --git a/tests/Filter/StringFilterTest.php b/tests/Filter/StringFilterTest.php index bc4c67a77..bb8a0dff4 100644 --- a/tests/Filter/StringFilterTest.php +++ b/tests/Filter/StringFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\StringOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\StringFilter; @@ -26,7 +27,7 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -63,7 +64,7 @@ public function testDefaultType($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => null]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => null])); if ('' !== (string) $value) { $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); @@ -88,7 +89,7 @@ public function testContains($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_CONTAINS]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_CONTAINS])); if ('' !== (string) $value) { $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); @@ -113,7 +114,7 @@ public function testStartsWith($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_STARTS_WITH]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_STARTS_WITH])); if ('' !== (string) $value) { $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); @@ -138,7 +139,7 @@ public function testEndsWith($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_ENDS_WITH]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_ENDS_WITH])); if ('' !== (string) $value) { $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); @@ -163,7 +164,7 @@ public function testNotContains($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_NOT_CONTAINS]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_NOT_CONTAINS])); if ('' !== (string) $value) { $this->assertSameQuery(['WHERE alias.field NOT LIKE :field_name_0 OR alias.field IS NULL'], $proxyQuery); @@ -188,7 +189,7 @@ public function testEquals($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_EQUAL]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_EQUAL])); if ('' !== (string) $value || $allowEmpty) { $this->assertSameQuery(['WHERE alias.field = :field_name_0'], $proxyQuery); @@ -213,7 +214,7 @@ public function testNotEquals($value, bool $allowEmpty): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => StringOperatorType::TYPE_NOT_EQUAL]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => StringOperatorType::TYPE_NOT_EQUAL])); if ('' !== (string) $value || $allowEmpty) { $this->assertSameQuery(['WHERE alias.field <> :field_name_0 OR alias.field IS NULL'], $proxyQuery); @@ -246,7 +247,7 @@ public function testEqualsWithValidParentAssociationMappings(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->apply($proxyQuery, ['type' => StringOperatorType::TYPE_EQUAL, 'value' => 'asd']); + $filter->apply($proxyQuery, FilterData::fromArray(['type' => StringOperatorType::TYPE_EQUAL, 'value' => 'asd'])); $this->assertSameQuery([ 'LEFT JOIN o.association_mapping AS s_association_mapping', @@ -271,7 +272,7 @@ public function testCaseSensitive(array $options, int $operatorType, string $exp $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'FooBar', 'type' => $operatorType]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'FooBar', 'type' => $operatorType])); $this->assertSameQuery([$expectedQuery], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => $expectedParameter], $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/StringListFilterTest.php b/tests/Filter/StringListFilterTest.php index 97e0a5e7e..389d748c2 100644 --- a/tests/Filter/StringListFilterTest.php +++ b/tests/Filter/StringListFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\AdminBundle\Form\Type\Operator\ContainsOperatorType; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\StringListFilter; @@ -26,7 +27,7 @@ public function testItStaysDisabledWhenFilteringWithAnEmptyValue(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); @@ -40,7 +41,7 @@ public function testFilteringWithNullReturnsArraysThatContainNull(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => null, 'type' => null]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => null, 'type' => null])); $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '%N;%'], $proxyQuery); $this->assertTrue($filter->isActive()); @@ -57,7 +58,7 @@ public function testContains(?int $type): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'asd', 'type' => $type]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'asd', 'type' => $type])); $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '%s:3:"asd";%'], $proxyQuery); $this->assertTrue($filter->isActive()); @@ -80,7 +81,7 @@ public function testNotContains(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'asd', 'type' => ContainsOperatorType::TYPE_NOT_CONTAINS]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'asd', 'type' => ContainsOperatorType::TYPE_NOT_CONTAINS])); $this->assertSameQuery(['WHERE alias.field NOT LIKE :field_name_0'], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '%s:3:"asd";%'], $proxyQuery); $this->assertTrue($filter->isActive()); @@ -94,7 +95,7 @@ public function testEquals(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => 'asd', 'type' => ContainsOperatorType::TYPE_EQUAL]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => 'asd', 'type' => ContainsOperatorType::TYPE_EQUAL])); $this->assertSameQuery(['WHERE alias.field LIKE :field_name_0 AND alias.field LIKE \'a:1:%\''], $proxyQuery); $this->assertSameQueryParameters(['field_name_0' => '%s:3:"asd";%'], $proxyQuery); $this->assertTrue($filter->isActive()); @@ -115,7 +116,7 @@ public function testMultipleValues(array $value, ?int $type, array $query, array $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); $this->assertSameQuery([], $proxyQuery); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => $value, 'type' => $type]); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => $value, 'type' => $type])); $this->assertSameQuery($query, $proxyQuery); $this->assertSameQueryParameters($parameters, $proxyQuery); $this->assertTrue($filter->isActive()); diff --git a/tests/Filter/TimeFilterTest.php b/tests/Filter/TimeFilterTest.php index 9286fa55d..98ed09634 100644 --- a/tests/Filter/TimeFilterTest.php +++ b/tests/Filter/TimeFilterTest.php @@ -13,6 +13,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Filter; +use Sonata\AdminBundle\Filter\Model\FilterData; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; use Sonata\DoctrineORMAdminBundle\Filter\TimeFilter; use Symfony\Component\Form\Extension\Core\Type\TimeType; @@ -29,8 +30,8 @@ public function testEmpty(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', []); - $filter->filter($proxyQuery, 'alias', 'field', ['value' => '']); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['value' => ''])); $this->assertSameQuery([], $proxyQuery); $this->assertFalse($filter->isActive()); diff --git a/tests/Functional/SimplePagerTest.php b/tests/Functional/SimplePagerTest.php new file mode 100644 index 000000000..54e41df18 --- /dev/null +++ b/tests/Functional/SimplePagerTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sonata\DoctrineORMAdminBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\Request; + +final class SimplePagerTest extends BaseFunctionalTestCase +{ + public function testSimplePagerSameResultsAsPager(): void + { + $crawlerWithPager = $this->client->request(Request::METHOD_GET, '/admin/tests/app/author/list'); + + $numberOfAuthors = $crawlerWithPager->filter('.js-author-item')->count(); + + $crawlerWithSimplePager = $this->client->request(Request::METHOD_GET, '/admin/author-with-simple-pager/list'); + + $this->assertCount($numberOfAuthors, $crawlerWithSimplePager->filter('.js-author-item')); + } +} diff --git a/tests/Model/ModelManagerTest.php b/tests/Model/ModelManagerTest.php index 7f81bd2bb..674c16888 100644 --- a/tests/Model/ModelManagerTest.php +++ b/tests/Model/ModelManagerTest.php @@ -14,7 +14,7 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Model; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\PostgreSqlPlatform; use Doctrine\DBAL\Types\Type; @@ -59,7 +59,7 @@ final class ModelManagerTest extends TestCase private $registry; /** - * @var ModelManager + * @var ModelManager */ private $modelManager; @@ -555,7 +555,7 @@ public function createUpdateRemoveData(): iterable new \PDOException(), ], 'DBALException' => [ - new DBALException(), + new Exception(), ], ]; }