From d6a3753accc45df44edd2008c0862a0498ff7df4 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 19 Feb 2021 17:38:15 +0100 Subject: [PATCH 01/58] work in progress --- src/ContaoManager/Plugin.php | 9 +- .../FilterConfigElementContainer.php | 39 +++++++++ src/DependencyInjection/Configuration.php | 4 + .../LoadDataContainerListener.php | 14 +++- src/Filter/Filter.php | 27 ++++++ src/FilterType/AbstractFilterType.php | 32 +++++++ src/FilterType/FilterTypeContext.php | 80 ++++++++++++++++++ src/FilterType/FilterTypeInterface.php | 18 ++++ src/FilterType/InitialFilterTypeInterface.php | 14 ++++ src/FilterType/Type/ButtonType.php | 29 +++++++ src/FilterType/Type/ChoiceType.php | 29 +++++++ src/FilterType/Type/DateTimeType.php | 29 +++++++ src/FilterType/Type/TextType.php | 83 +++++++++++++++++++ src/Resources/config/config.yml | 1 + src/Resources/config/datacontainer.yml | 9 ++ src/Resources/config/services.yml | 23 +++-- .../contao/dca/tl_filter_config_element.php | 5 +- 17 files changed, 435 insertions(+), 10 deletions(-) create mode 100644 src/DataContainer/FilterConfigElementContainer.php create mode 100644 src/Filter/Filter.php create mode 100644 src/FilterType/AbstractFilterType.php create mode 100644 src/FilterType/FilterTypeContext.php create mode 100644 src/FilterType/FilterTypeInterface.php create mode 100644 src/FilterType/InitialFilterTypeInterface.php create mode 100644 src/FilterType/Type/ButtonType.php create mode 100644 src/FilterType/Type/ChoiceType.php create mode 100644 src/FilterType/Type/DateTimeType.php create mode 100644 src/FilterType/Type/TextType.php create mode 100644 src/Resources/config/datacontainer.yml diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 1ca4c42c..4de219d9 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -12,16 +12,18 @@ use Contao\ManagerPlugin\Bundle\BundlePluginInterface; use Contao\ManagerPlugin\Bundle\Config\BundleConfig; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; +use Contao\ManagerPlugin\Config\ConfigPluginInterface; use Contao\ManagerPlugin\Config\ContainerBuilder; use Contao\ManagerPlugin\Config\ExtensionPluginInterface; use Contao\ManagerPlugin\Routing\RoutingPluginInterface; use HeimrichHannot\FilterBundle\HeimrichHannotContaoFilterBundle; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\RouteCollection; -class Plugin implements BundlePluginInterface, ExtensionPluginInterface, RoutingPluginInterface +class Plugin implements BundlePluginInterface, ExtensionPluginInterface, RoutingPluginInterface, ConfigPluginInterface { /** * {@inheritdoc} @@ -77,4 +79,9 @@ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInte ->resolve(__DIR__.'/../Resources/config/routing.yml') ->load(__DIR__.'/../Resources/config/routing.yml'); } + + public function registerContainerConfiguration(LoaderInterface $loader, array $managerConfig) + { + $loader->load('@HeimrichHannotContaoFilterBundle/Resources/config/datacontainer.yml'); + } } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php new file mode 100644 index 00000000..7f490fcc --- /dev/null +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -0,0 +1,39 @@ +bundleConfig = $bundleConfig; + $this->typeChoice = $typeChoice; + } + + public function getTypeOptions(DataContainer $dc) + { + if ($this->bundleConfig['filter']['disable_legacy_filters']) { + return ['text' => ['future_text']]; + } + + return $this->typeChoice->getCachedChoices($dc); + } +} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 5b4873f7..58cf5abc 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -39,6 +39,10 @@ public function getConfigTreeBuilder() ->arrayNode('filter') ->addDefaultsIfNotSet() ->children() + ->booleanNode('disable_legacy_filters') + ->defaultFalse() + ->info('Disable legacy filters to be able to use new implementation of filters together with HTTP GET requests.') + ->end() ->arrayNode('types') ->arrayPrototype() ->children() diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index f00e279d..80e7b1ec 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -9,17 +9,23 @@ namespace HeimrichHannot\FilterBundle\EventListener; use Doctrine\DBAL\Connection; +use Psr\Container\ContainerInterface; class LoadDataContainerListener { + /** + * @var ContainerInterface + */ + protected ContainerInterface $locator; /** * @var Connection */ private $connection; - public function __construct(Connection $connection) + public function __construct(Connection $connection, ContainerInterface $locator) { $this->connection = $connection; + $this->locator = $locator; } /** @@ -32,5 +38,11 @@ public function onLoadDataContainer(string $table): void $this->connection->executeQuery("ALTER TABLE tl_filter_config CHANGE action filterFormAction VARCHAR(255) DEFAULT '' NOT NULL"); } } + + if ('tl_filter_config_element' === $table) { + $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; +// $textType = $this->locator->get('huh.filter.filter_type.type.text_type'); +// $dca['palettes'][$textType::TYPE] = $textType->getPalette(); + } } } diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php new file mode 100644 index 00000000..ae352fac --- /dev/null +++ b/src/Filter/Filter.php @@ -0,0 +1,27 @@ +getReflectionClass()->name) { + case 'HeimrichHannot/FilterBundle/FilterType/Type/TextType': + return sprintf('%s.name = %s', $targetTableAlias, $this->getParameter('name')); + + break; + } + + return ''; + } +} diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php new file mode 100644 index 00000000..038504cf --- /dev/null +++ b/src/FilterType/AbstractFilterType.php @@ -0,0 +1,32 @@ +context; + } + + public function setContext(FilterTypeContext $context) + { + $this->context = $context; + } + + public function getPalette(): string + { + return '{general_legend},title;{expert_legend},cssClass;{publish_legend},published;'; + } +} diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php new file mode 100644 index 00000000..e2aa47c2 --- /dev/null +++ b/src/FilterType/FilterTypeContext.php @@ -0,0 +1,80 @@ +name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getDefaultValue(): string + { + return $this->defaultValue; + } + + public function setDefaultValue(string $defaultValue): void + { + $this->defaultValue = $defaultValue; + } + + public function getValue(): string + { + return $this->value; + } + + public function setValue(string $value): void + { + $this->value = $value; + } + + public function getContext(): self + { + return $this; + } + + public function getIterator(): \ArrayIterator + { + return new \ArrayIterator($this); + } + + public function isInitial(): bool + { + return $this->initial; + } + + public function setInitial(): void + { + $this->initial = true; + } +} diff --git a/src/FilterType/FilterTypeInterface.php b/src/FilterType/FilterTypeInterface.php new file mode 100644 index 00000000..03c1c2bd --- /dev/null +++ b/src/FilterType/FilterTypeInterface.php @@ -0,0 +1,18 @@ +em = $em; + $this->filter = $filter; + } + + public static function getType(): string + { + return static::TYPE; + } + + public function buildQuery(FilterTypeContext $filterTypeContext) + { + try { + foreach ($filterTypeContext->getIterator() as $param) { + $this->filter->setParameter($param->key(), $param->current()); + } + } catch (\Exception $e) { + throw new \Exception($e->getMessage()); + } + + $this->em->getFilters()->enable('huh_filter'); + } + + public function buildForm($filterTypeContext) + { + // TODO: Implement buildForm() method. + } + + public function getPalette(): string + { + return '{general_legend},title,type;{config_legend},field'; +// return parent::getPalette($filterTypeContext); + } + + public function preparePalette($filterTypeContext): void + { +// '{general_legend},type,isInitial;{config_legend},field,customName,customOperator,addDefaultValue,submitOnInput;{visualization_legend},addPlaceholder,customLabel,hideLabel,inputGroup;', + $paletteManipulator = PaletteManipulator::create(); + + $paletteManipulator->addField( + ($filterTypeContext->isInitial() ? 'initialType' : 'type'), + 'general_legend', + PaletteManipulator::POSITION_APPEND); + + $paletteManipulator->applyToPalette(static::TYPE, 'tl_filter_config_element'); + } + + public function getInitialPalette(FilterTypeContext $filterTypeContext) + { + // TODO: Implement getInitialPalette() method. + } +} diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index 398f6c58..dabbe834 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,5 +1,6 @@ huh: filter: + disable_legacy_filters: true types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } diff --git a/src/Resources/config/datacontainer.yml b/src/Resources/config/datacontainer.yml new file mode 100644 index 00000000..7bb29311 --- /dev/null +++ b/src/Resources/config/datacontainer.yml @@ -0,0 +1,9 @@ +services: + _defaults: + public: true + autowire: true + bind: + $bundleConfig: '%huh.filter%' + + HeimrichHannot\FilterBundle\DataContainer\: + resource: '../../DataContainer/*' \ No newline at end of file diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index cc150177..5af7b12d 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -1,4 +1,8 @@ services: + _defaults: + public: true + autowire: true + huh.form_type_extension: class: HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension tags: @@ -67,11 +71,12 @@ services: arguments: - "@contao.framework" - huh.filter.choice.type: - class: HeimrichHannot\FilterBundle\Choice\TypeChoice - public: true - arguments: - - "@contao.framework" + HeimrichHannot\FilterBundle\Choice\TypeChoice: ~ + huh.filter.choice.type: '@HeimrichHannot\FilterBundle\Choice\TypeChoice' +# class: HeimrichHannot\FilterBundle\Choice\TypeChoice +# public: true +# arguments: +# - "@contao.framework" huh.filter.choice.field_options: class: HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice @@ -169,3 +174,11 @@ services: tags: - { name: twig.extension } + HeimrichHannot\FilterBundle\FilterType\Type\TextType: ~ + HeimrichHannot\FilterBundle\Filter\Filter: ~ + + huh.filter.filter_type_locator: + class: Symfony\Component\DependencyInjection\ServiceLocator + tags: ['container.service_locator'] + arguments: + - 'textType': '@HeimrichHannot\FilterBundle\FilterType\Type\TextType' \ No newline at end of file diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 37d38355..30bde0e9 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -157,6 +157,7 @@ \HeimrichHannot\FilterBundle\Filter\Type\SortType::TYPE => '{general_legend},title,type;{config_legend},sortOptions,expanded,submitOnChange;{visualization_legend},addPlaceholder,customLabel,hideLabel;{publish_legend},published', \HeimrichHannot\FilterBundle\Filter\Type\ExternalEntityType::TYPE => '{general_legend},title,type;{source_legend},sourceTable,sourceField,sourceEntityResolve,sourceEntityOverridesOrder;{config_legend},field,customOperator;{publish_legend},published;', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE => '{general_legend},title,type;{config_legend},field,customOperator,currentUserAssign;{publish_legend},published', + \HeimrichHannot\FilterBundle\FilterType\Type\TextType::TYPE => '', ], 'subpalettes' => [ 'customOptions' => 'options', @@ -216,9 +217,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { - return \Contao\System::getContainer()->get('huh.filter.choice.type')->getCachedChoices($dc); - }, + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getTypeOptions'], 'reference' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['reference']['type'], 'eval' => [ 'chosen' => true, From a659db9cbf55bc227c51c475d1b0b46544b01ac6 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 22 Feb 2021 13:53:26 +0100 Subject: [PATCH 02/58] added filterTypes to backend selection --- .../FilterConfigElementContainer.php | 23 ++++++- .../LoadDataContainerListener.php | 16 +++-- src/FilterType/AbstractFilterType.php | 15 +++++ src/FilterType/FilterTypeCollection.php | 67 +++++++++++++++++++ src/FilterType/Type/ButtonType.php | 7 ++ src/FilterType/Type/ChoiceType.php | 7 ++ src/FilterType/Type/DateTimeType.php | 7 ++ src/FilterType/Type/TextType.php | 46 ++++++++----- src/Resources/config/listener.yml | 3 +- src/Resources/config/services.yml | 11 +-- .../contao/dca/tl_filter_config_element.php | 1 - .../languages/de/tl_filter_config_element.php | 1 + 12 files changed, 170 insertions(+), 34 deletions(-) create mode 100644 src/FilterType/FilterTypeCollection.php diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 7f490fcc..915eac69 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -10,9 +10,12 @@ use Contao\DataContainer; use HeimrichHannot\FilterBundle\Choice\TypeChoice; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; class FilterConfigElementContainer { + const GROUP_DEFAULT = 'miscellaneous'; + /** * @var array */ @@ -21,17 +24,33 @@ class FilterConfigElementContainer * @var TypeChoice */ protected $typeChoice; + /** + * @var FilterTypeCollection + */ + protected $typeCollection; - public function __construct(array $bundleConfig, TypeChoice $typeChoice) + public function __construct(array $bundleConfig, TypeChoice $typeChoice, FilterTypeCollection $typeCollection) { $this->bundleConfig = $bundleConfig; $this->typeChoice = $typeChoice; + $this->typeCollection = $typeCollection; } public function getTypeOptions(DataContainer $dc) { if ($this->bundleConfig['filter']['disable_legacy_filters']) { - return ['text' => ['future_text']]; + $options = []; + + foreach ($this->typeCollection->getTypes() as $key => $type) { + $group = $type->getGroup(); + + if (empty($group)) { + $group = static::GROUP_DEFAULT; + } + $options[$group][] = $key; + } + + return $options; } return $this->typeChoice->getCachedChoices($dc); diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index 80e7b1ec..75ae86e7 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -9,23 +9,23 @@ namespace HeimrichHannot\FilterBundle\EventListener; use Doctrine\DBAL\Connection; -use Psr\Container\ContainerInterface; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; class LoadDataContainerListener { /** - * @var ContainerInterface + * @var FilterTypeCollection */ - protected ContainerInterface $locator; + protected $filterTypeCollection; /** * @var Connection */ private $connection; - public function __construct(Connection $connection, ContainerInterface $locator) + public function __construct(Connection $connection, FilterTypeCollection $filterTypeCollection) { $this->connection = $connection; - $this->locator = $locator; + $this->filterTypeCollection = $filterTypeCollection; } /** @@ -41,8 +41,10 @@ public function onLoadDataContainer(string $table): void if ('tl_filter_config_element' === $table) { $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; -// $textType = $this->locator->get('huh.filter.filter_type.type.text_type'); -// $dca['palettes'][$textType::TYPE] = $textType->getPalette(); + $types = $this->filterTypeCollection->getTypes(); + + $textType = $this->filterTypeCollection->getType('future_text'); + $dca['palettes'][$textType::TYPE] = $textType->getPalette(); } } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 038504cf..e0dbcf1d 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -15,6 +15,11 @@ abstract class AbstractFilterType implements FilterTypeInterface */ private $context; + /** + * @var string + */ + private $group = ''; + public function getContext(): FilterTypeContext { return $this->context; @@ -29,4 +34,14 @@ public function getPalette(): string { return '{general_legend},title;{expert_legend},cssClass;{publish_legend},published;'; } + + public function getGroup(): string + { + return $this->group; + } + + public function setGroup(string $group): void + { + $this->group = $group; + } } diff --git a/src/FilterType/FilterTypeCollection.php b/src/FilterType/FilterTypeCollection.php new file mode 100644 index 00000000..a22f8827 --- /dev/null +++ b/src/FilterType/FilterTypeCollection.php @@ -0,0 +1,67 @@ +typeIterable = $typeIterable; + } + + public function getTypes(): array + { + if (!$this->types) { + $this->types = []; + + foreach ($this->typeIterable as $type) { + $this->types[$type::getType()] = $type; + } + } + + return $this->types; + } + + public function getInitialTypes(): array + { + if (!$this->types) { + $this->types = []; + + foreach ($this->typeIterable as $type) { + $this->types[$type::getType()] = $type; + } + } + + return $this->types; + } + + public function hasType(string $type): bool + { + return isset($this->getTypes()[$type]); + } + + public function getType(string $type): ?FilterTypeInterface + { + if ($this->hasType($type)) { + return $this->getTypes()[$type]; + } + + return null; + } +} diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index c2e4cf9f..8e140401 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -12,6 +12,13 @@ class ButtonType extends AbstractFilterType { + const TYPE = 'future_button'; + + public static function getType(): string + { + return static::TYPE; + } + public function buildQuery($filterTypeContext): string { // TODO: Implement buildQuery() method. diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index f24ad805..1524f7c1 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -12,6 +12,13 @@ class ChoiceType extends AbstractFilterType { + const TYPE = 'future_choice'; + + public static function getType(): string + { + return static::TYPE; + } + public function buildQuery($filterTypeContext): string { // TODO: Implement buildQuery() method. diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 313ee69d..7d1dea9a 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -12,6 +12,13 @@ class DateTimeType extends AbstractFilterType { + const TYPE = 'future_date_time'; + + public static function getType(): string + { + return static::TYPE; + } + public function buildQuery($filterTypeContext): string { // TODO: Implement buildQuery() method. diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 8c43e518..0cd28f64 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -18,20 +18,22 @@ class TextType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'future_text'; + const GROUP = 'text'; /** * @var EntityManagerInterface */ - protected EntityManagerInterface $em; + protected $em; /** * @var Filter */ - protected Filter $filter; + protected $filter; public function __construct(Filter $filter, EntityManagerInterface $em) { $this->em = $em; $this->filter = $filter; + $this->initialize(); } public static function getType(): string @@ -52,32 +54,40 @@ public function buildQuery(FilterTypeContext $filterTypeContext) $this->em->getFilters()->enable('huh_filter'); } - public function buildForm($filterTypeContext) + public function buildForm(FilterTypeContext $filterTypeContext) { - // TODO: Implement buildForm() method. } public function getPalette(): string { - return '{general_legend},title,type;{config_legend},field'; -// return parent::getPalette($filterTypeContext); - } + if ($this->getContext()->isInitial()) { + return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field'; + } - public function preparePalette($filterTypeContext): void - { -// '{general_legend},type,isInitial;{config_legend},field,customName,customOperator,addDefaultValue,submitOnInput;{visualization_legend},addPlaceholder,customLabel,hideLabel,inputGroup;', - $paletteManipulator = PaletteManipulator::create(); + return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field'; + } - $paletteManipulator->addField( - ($filterTypeContext->isInitial() ? 'initialType' : 'type'), - 'general_legend', - PaletteManipulator::POSITION_APPEND); +// public function preparePalette($filterTypeContext): void +// { + //// '{general_legend},type,isInitial;{config_legend},field,customName,customOperator,addDefaultValue,submitOnInput;{visualization_legend},addPlaceholder,customLabel,hideLabel,inputGroup;', +// $paletteManipulator = PaletteManipulator::create(); +// +// $paletteManipulator->addField( +// ($filterTypeContext->isInitial() ? 'initialType' : 'type'), +// 'general_legend', +// PaletteManipulator::POSITION_APPEND); +// +// $paletteManipulator->applyToPalette(static::TYPE, 'tl_filter_config_element'); +// } - $paletteManipulator->applyToPalette(static::TYPE, 'tl_filter_config_element'); + public function getInitialPalette(FilterTypeContext $filterTypeContext): string + { + return '{initial_legend},isInitial;{general_legend},title;{config_legend},field'; } - public function getInitialPalette(FilterTypeContext $filterTypeContext) + private function initialize(): void { - // TODO: Implement getInitialPalette() method. + $this->setContext(new FilterTypeContext()); + $this->setGroup(static::GROUP); } } diff --git a/src/Resources/config/listener.yml b/src/Resources/config/listener.yml index 43eec779..3407b564 100644 --- a/src/Resources/config/listener.yml +++ b/src/Resources/config/listener.yml @@ -3,7 +3,8 @@ services: public: true autowire: true - HeimrichHannot\FilterBundle\EventListener\LoadDataContainerListener: ~ + HeimrichHannot\FilterBundle\EventListener\LoadDataContainerListener: + autoconfigure: true huh.filter.listener.dca.callback.filterconfigelement: class: HeimrichHannot\FilterBundle\EventListener\FilterConfigElementCallbackListener diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 5af7b12d..c997df66 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -174,11 +174,12 @@ services: tags: - { name: twig.extension } - HeimrichHannot\FilterBundle\FilterType\Type\TextType: ~ + HeimrichHannot\FilterBundle\FilterType\Type\: + resource: '../../FilterType/Type/*' + tags: ['huh.filter.filter_type'] + HeimrichHannot\FilterBundle\Filter\Filter: ~ - huh.filter.filter_type_locator: - class: Symfony\Component\DependencyInjection\ServiceLocator - tags: ['container.service_locator'] + HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection: arguments: - - 'textType': '@HeimrichHannot\FilterBundle\FilterType\Type\TextType' \ No newline at end of file + - !tagged huh.filter.filter_type \ No newline at end of file diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 30bde0e9..d0336761 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -157,7 +157,6 @@ \HeimrichHannot\FilterBundle\Filter\Type\SortType::TYPE => '{general_legend},title,type;{config_legend},sortOptions,expanded,submitOnChange;{visualization_legend},addPlaceholder,customLabel,hideLabel;{publish_legend},published', \HeimrichHannot\FilterBundle\Filter\Type\ExternalEntityType::TYPE => '{general_legend},title,type;{source_legend},sourceTable,sourceField,sourceEntityResolve,sourceEntityOverridesOrder;{config_legend},field,customOperator;{publish_legend},published;', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE => '{general_legend},title,type;{config_legend},field,customOperator,currentUserAssign;{publish_legend},published', - \HeimrichHannot\FilterBundle\FilterType\Type\TextType::TYPE => '', ], 'subpalettes' => [ 'customOptions' => 'options', diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index 85c96cc6..51a51fa9 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -150,6 +150,7 @@ */ $lang['reference'] = [ 'type' => [ + 'miscellaneous' => 'Sonstiges', 'text' => 'Text', 'text_concat' => 'Konkatenierter Text', 'textarea' => 'Textarea', From 630d39957f7f215a656904873676092147ea9f7c Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 22 Feb 2021 15:55:40 +0100 Subject: [PATCH 03/58] added difference between initial and not initial filters --- .../FilterConfigElementContainer.php | 25 +++++++++++-------- src/DependencyInjection/Configuration.php | 6 +++++ .../LoadDataContainerListener.php | 13 ++++++++-- src/FilterType/AbstractFilterType.php | 4 +-- src/FilterType/FilterTypeInterface.php | 2 +- src/FilterType/InitialFilterTypeInterface.php | 2 +- src/FilterType/Type/ButtonType.php | 5 ++-- src/FilterType/Type/ChoiceType.php | 5 ++-- src/FilterType/Type/DateTimeType.php | 5 ++-- src/FilterType/Type/TextType.php | 25 ++++--------------- src/Resources/config/config.yml | 1 + 11 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 915eac69..fbd26fac 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -38,21 +38,26 @@ public function __construct(array $bundleConfig, TypeChoice $typeChoice, FilterT public function getTypeOptions(DataContainer $dc) { - if ($this->bundleConfig['filter']['disable_legacy_filters']) { - $options = []; + if (!$this->bundleConfig['filter']['disable_legacy_filters']) { + return $this->typeChoice->getCachedChoices($dc); + } - foreach ($this->typeCollection->getTypes() as $key => $type) { - $group = $type->getGroup(); + $options = []; - if (empty($group)) { - $group = static::GROUP_DEFAULT; - } - $options[$group][] = $key; + foreach ($this->typeCollection->getTypes() as $key => $type) { + $group = $type->getGroup(); + + if (empty($group)) { + $group = static::GROUP_DEFAULT; } - return $options; + if ($dc->activeRecord->isInitial && \in_array($key, $this->bundleConfig['filter']['initial_types'])) { + $options[$group][] = $key; + } elseif (!$dc->activeRecord->isInitial) { + $options[$group][] = $key; + } } - return $this->typeChoice->getCachedChoices($dc); + return $options; } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 58cf5abc..332dff3e 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -43,6 +43,12 @@ public function getConfigTreeBuilder() ->defaultFalse() ->info('Disable legacy filters to be able to use new implementation of filters together with HTTP GET requests.') ->end() + ->arrayNode('initial_types') + ->scalarPrototype() + ->defaultValue([]) + ->info('Filter types that can be used as initial.') + ->end() + ->end() ->arrayNode('types') ->arrayPrototype() ->children() diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index 75ae86e7..d4c0c69e 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class LoadDataContainerListener { @@ -43,8 +44,16 @@ public function onLoadDataContainer(string $table): void $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; $types = $this->filterTypeCollection->getTypes(); - $textType = $this->filterTypeCollection->getType('future_text'); - $dca['palettes'][$textType::TYPE] = $textType->getPalette(); + $filterTypeContext = $this->getFilterTypeContext(); + + foreach ($types as $key => $type) { + $dca['palettes'][$key] = $filterTypeContext; + } } } + + private function getFilterTypeContext(): FilterTypeContext + { + return new FilterTypeContext(); + } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index e0dbcf1d..d20c1048 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -30,9 +30,9 @@ public function setContext(FilterTypeContext $context) $this->context = $context; } - public function getPalette(): string + public function getPalette(FilterTypeContext $context): string { - return '{general_legend},title;{expert_legend},cssClass;{publish_legend},published;'; + return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;{expert_legend},cssClass;{publish_legend},published;'; } public function getGroup(): string diff --git a/src/FilterType/FilterTypeInterface.php b/src/FilterType/FilterTypeInterface.php index 03c1c2bd..6bd5612c 100644 --- a/src/FilterType/FilterTypeInterface.php +++ b/src/FilterType/FilterTypeInterface.php @@ -14,5 +14,5 @@ public function buildQuery(FilterTypeContext $filterTypeContext); public function buildForm(FilterTypeContext $filterTypeContext); - public function getPalette(): string; + public function getPalette(FilterTypeContext $context): string; } diff --git a/src/FilterType/InitialFilterTypeInterface.php b/src/FilterType/InitialFilterTypeInterface.php index 13196358..cb4edd6b 100644 --- a/src/FilterType/InitialFilterTypeInterface.php +++ b/src/FilterType/InitialFilterTypeInterface.php @@ -10,5 +10,5 @@ interface InitialFilterTypeInterface { - public function getInitialPalette(FilterTypeContext $filterTypeContext); + public function getInitialPalette(); } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index 8e140401..b60e3c9d 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ButtonType extends AbstractFilterType { @@ -29,8 +30,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(FilterTypeContext $context): string { - // TODO: Implement getPalette() method. + return parent::getPalette($context); } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index 1524f7c1..6beb033b 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ChoiceType extends AbstractFilterType { @@ -29,8 +30,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(FilterTypeContext $context): string { - // TODO: Implement getPalette() method. + return parent::getPalette($context); } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 7d1dea9a..850b00b5 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class DateTimeType extends AbstractFilterType { @@ -29,8 +30,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(FilterTypeContext $context): string { - // TODO: Implement getPalette() method. + return parent::getPalette($context); } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 0cd28f64..958bf68b 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -8,7 +8,6 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; -use Contao\CoreBundle\DataContainer\PaletteManipulator; use Doctrine\ORM\EntityManagerInterface; use HeimrichHannot\FilterBundle\Filter\Filter; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; @@ -58,36 +57,22 @@ public function buildForm(FilterTypeContext $filterTypeContext) { } - public function getPalette(): string + public function getPalette(FilterTypeContext $context): string { if ($this->getContext()->isInitial()) { - return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field'; + return $this->getInitialPalette(); } - return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field'; + return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;{expert_legend},cssClass;{publish_legend},published;'; } -// public function preparePalette($filterTypeContext): void -// { - //// '{general_legend},type,isInitial;{config_legend},field,customName,customOperator,addDefaultValue,submitOnInput;{visualization_legend},addPlaceholder,customLabel,hideLabel,inputGroup;', -// $paletteManipulator = PaletteManipulator::create(); -// -// $paletteManipulator->addField( -// ($filterTypeContext->isInitial() ? 'initialType' : 'type'), -// 'general_legend', -// PaletteManipulator::POSITION_APPEND); -// -// $paletteManipulator->applyToPalette(static::TYPE, 'tl_filter_config_element'); -// } - - public function getInitialPalette(FilterTypeContext $filterTypeContext): string + public function getInitialPalette(): string { - return '{initial_legend},isInitial;{general_legend},title;{config_legend},field'; + return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;'; } private function initialize(): void { - $this->setContext(new FilterTypeContext()); $this->setGroup(static::GROUP); } } diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index dabbe834..12f1c396 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,6 +1,7 @@ huh: filter: disable_legacy_filters: true + initial_types: [ 'future_text' ] types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } From 9ae57b52c26c6be4fe21500fd93fe1d6645a8bd6 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 26 Feb 2021 12:02:46 +0100 Subject: [PATCH 04/58] refactored context assignemnt --- .../LoadDataContainerListener.php | 19 ++++++++----------- src/FilterType/AbstractFilterType.php | 12 +++++++++++- src/FilterType/FilterTypeInterface.php | 2 +- src/FilterType/Type/ButtonType.php | 5 ++--- src/FilterType/Type/ChoiceType.php | 5 ++--- src/FilterType/Type/DateTimeType.php | 5 ++--- src/FilterType/Type/TextType.php | 2 +- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index d4c0c69e..e5f5c080 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -10,7 +10,6 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class LoadDataContainerListener { @@ -41,19 +40,17 @@ public function onLoadDataContainer(string $table): void } if ('tl_filter_config_element' === $table) { - $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; - $types = $this->filterTypeCollection->getTypes(); - - $filterTypeContext = $this->getFilterTypeContext(); - - foreach ($types as $key => $type) { - $dca['palettes'][$key] = $filterTypeContext; - } + $this->prepareFilterConfigElementDca(); } } - private function getFilterTypeContext(): FilterTypeContext + private function prepareFilterConfigElementDca() { - return new FilterTypeContext(); + $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; + $types = $this->filterTypeCollection->getTypes(); + + foreach ($types as $key => $type) { + $dca['palettes'][$key] = $type->getPalette(); + } } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index d20c1048..40d635a6 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -22,6 +22,10 @@ abstract class AbstractFilterType implements FilterTypeInterface public function getContext(): FilterTypeContext { + if (!isset($this->context)) { + $this->setDefaultContext(); + } + return $this->context; } @@ -30,7 +34,7 @@ public function setContext(FilterTypeContext $context) $this->context = $context; } - public function getPalette(FilterTypeContext $context): string + public function getPalette(): string { return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;{expert_legend},cssClass;{publish_legend},published;'; } @@ -44,4 +48,10 @@ public function setGroup(string $group): void { $this->group = $group; } + + private function setDefaultContext() + { + $context = new FilterTypeContext(); + $this->context = $context; + } } diff --git a/src/FilterType/FilterTypeInterface.php b/src/FilterType/FilterTypeInterface.php index 6bd5612c..03c1c2bd 100644 --- a/src/FilterType/FilterTypeInterface.php +++ b/src/FilterType/FilterTypeInterface.php @@ -14,5 +14,5 @@ public function buildQuery(FilterTypeContext $filterTypeContext); public function buildForm(FilterTypeContext $filterTypeContext); - public function getPalette(FilterTypeContext $context): string; + public function getPalette(): string; } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index b60e3c9d..02435681 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -9,7 +9,6 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ButtonType extends AbstractFilterType { @@ -30,8 +29,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(FilterTypeContext $context): string + public function getPalette(): string { - return parent::getPalette($context); + return parent::getPalette(); } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index 6beb033b..e4753ab3 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -9,7 +9,6 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ChoiceType extends AbstractFilterType { @@ -30,8 +29,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(FilterTypeContext $context): string + public function getPalette(): string { - return parent::getPalette($context); + return parent::getPalette(); } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 850b00b5..27dafc8c 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -9,7 +9,6 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class DateTimeType extends AbstractFilterType { @@ -30,8 +29,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(FilterTypeContext $context): string + public function getPalette(): string { - return parent::getPalette($context); + return parent::getPalette(); } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 958bf68b..5d456fe7 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -57,7 +57,7 @@ public function buildForm(FilterTypeContext $filterTypeContext) { } - public function getPalette(FilterTypeContext $context): string + public function getPalette(): string { if ($this->getContext()->isInitial()) { return $this->getInitialPalette(); From 84b2e07b0a9b55b138e082c2b62a788a22b1eef0 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 26 Feb 2021 17:19:30 +0100 Subject: [PATCH 05/58] refactoring --- src/Config/FilterConfig.php | 7 ++++ .../FilterConfigElementContainer.php | 33 ++++++++++++---- .../LoadDataContainerListener.php | 13 ++++++- src/FilterType/AbstractFilterType.php | 39 +++++++++++++++++-- src/FilterType/FilterTypeInterface.php | 2 +- src/FilterType/InitialFilterTypeInterface.php | 2 +- src/FilterType/Type/ButtonType.php | 6 +-- src/FilterType/Type/ChoiceType.php | 6 +-- src/FilterType/Type/DateTimeType.php | 11 ++++-- src/FilterType/Type/TextType.php | 37 +++--------------- src/Resources/config/config.yml | 1 - .../contao/dca/tl_filter_config_element.php | 1 + 12 files changed, 100 insertions(+), 58 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 8ceef87e..c66312f3 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -14,6 +14,8 @@ use Contao\InsertTags; use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Filter\AbstractType; +use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; use HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension; use HeimrichHannot\FilterBundle\Form\FilterType; @@ -227,6 +229,11 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip return; } +// if ($type instanceof AbstractFilterType) { +// $filterContext = new FilterTypeContext($element, $this->filterConfig); +// $typ->buildQuery($queryBuilder, $filterContext); +// } + if (!isset($types[$element->type]) || \in_array($element->id, $skipElements) || $mode === static::QUERY_BUILDER_MODE_INITIAL_ONLY && !$element->isInitial || $mode === static::QUERY_BUILDER_MODE_SKIP_INITIAL && $element->isInitial) { diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index fbd26fac..0f708348 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -11,11 +11,12 @@ use Contao\DataContainer; use HeimrichHannot\FilterBundle\Choice\TypeChoice; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; +use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\UtilsBundle\Container\ContainerUtil; class FilterConfigElementContainer { - const GROUP_DEFAULT = 'miscellaneous'; - /** * @var array */ @@ -28,12 +29,32 @@ class FilterConfigElementContainer * @var FilterTypeCollection */ protected $typeCollection; + /** + * @var ContainerUtil + */ + protected ContainerUtil $container; - public function __construct(array $bundleConfig, TypeChoice $typeChoice, FilterTypeCollection $typeCollection) + public function __construct(array $bundleConfig, TypeChoice $typeChoice, FilterTypeCollection $typeCollection, ContainerUtil $container) { $this->bundleConfig = $bundleConfig; $this->typeChoice = $typeChoice; $this->typeCollection = $typeCollection; + $this->container = $container; + } + + public function onLoadCallback(DataContainer $dc): void + { + if ('edit' === \Input::get('act') && $this->container->isBackend()) { + $model = FilterConfigElementModel::findByIdOrAlias($dc->id); + $type = $this->typeCollection->getType($model->type); + + if ($type instanceof InitialFilterTypeInterface && $model->isInitial) { + $prependPalette = '{initial_legend},isInitial;{general_legend},title,type;'; + $appendPalette = '{publish_legend},published;'; + + $dca['palettes'][$model->type] = $type->getInitialPalette($prependPalette, $appendPalette); + } + } } public function getTypeOptions(DataContainer $dc) @@ -47,11 +68,7 @@ public function getTypeOptions(DataContainer $dc) foreach ($this->typeCollection->getTypes() as $key => $type) { $group = $type->getGroup(); - if (empty($group)) { - $group = static::GROUP_DEFAULT; - } - - if ($dc->activeRecord->isInitial && \in_array($key, $this->bundleConfig['filter']['initial_types'])) { + if ($dc->activeRecord->isInitial && $type instanceof InitialFilterTypeInterface) { $options[$group][] = $key; } elseif (!$dc->activeRecord->isInitial) { $options[$group][] = $key; diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index e5f5c080..39447804 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -10,10 +10,11 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; +use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; class LoadDataContainerListener { - /** + /* * @var FilterTypeCollection */ protected $filterTypeCollection; @@ -50,7 +51,15 @@ private function prepareFilterConfigElementDca() $types = $this->filterTypeCollection->getTypes(); foreach ($types as $key => $type) { - $dca['palettes'][$key] = $type->getPalette(); + $prependPalette = '{general_legend},title,type;'; + + if ($type instanceof InitialFilterTypeInterface) { + $prependPalette = '{initial_legend},isInitial;'.$prependPalette; + } + + $appendPalette = '{publish_legend},published;'; + + $dca['palettes'][$key] = $type->getPalette($prependPalette, $appendPalette); } } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 40d635a6..95427d04 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -8,8 +8,24 @@ namespace HeimrichHannot\FilterBundle\FilterType; +use Doctrine\ORM\EntityManagerInterface; +use HeimrichHannot\FilterBundle\Filter\Filter; + abstract class AbstractFilterType implements FilterTypeInterface { + const GROUP_DEFAULT = 'miscellaneous'; + + const PREPEND_PALETTE = 'type,name'; + + /** + * @var EntityManagerInterface + */ + protected $em; + /** + * @var Filter + */ + protected $filter; + /** * @var FilterTypeContext */ @@ -20,6 +36,13 @@ abstract class AbstractFilterType implements FilterTypeInterface */ private $group = ''; + public function __construct(Filter $filter, EntityManagerInterface $em) + { + $this->em = $em; + $this->filter = $filter; + $this->initialize(); + } + public function getContext(): FilterTypeContext { if (!isset($this->context)) { @@ -34,9 +57,9 @@ public function setContext(FilterTypeContext $context) $this->context = $context; } - public function getPalette(): string + public function getPalette(string $prependPalette, string $appendPalette): string { - return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;{expert_legend},cssClass;{publish_legend},published;'; + return $prependPalette.$appendPalette; } public function getGroup(): string @@ -49,9 +72,17 @@ public function setGroup(string $group): void $this->group = $group; } + protected function initialize(): void + { + if (empty($this->group) && !\defined('static::GROUP')) { + $this->setGroup(static::GROUP_DEFAULT); + } else { + $this->setGroup(static::GROUP); + } + } + private function setDefaultContext() { - $context = new FilterTypeContext(); - $this->context = $context; + $this->context = new FilterTypeContext(); } } diff --git a/src/FilterType/FilterTypeInterface.php b/src/FilterType/FilterTypeInterface.php index 03c1c2bd..aa9687ea 100644 --- a/src/FilterType/FilterTypeInterface.php +++ b/src/FilterType/FilterTypeInterface.php @@ -14,5 +14,5 @@ public function buildQuery(FilterTypeContext $filterTypeContext); public function buildForm(FilterTypeContext $filterTypeContext); - public function getPalette(): string; + public function getPalette(string $prependPalette, string $appendPalette): string; } diff --git a/src/FilterType/InitialFilterTypeInterface.php b/src/FilterType/InitialFilterTypeInterface.php index cb4edd6b..a9e8fac5 100644 --- a/src/FilterType/InitialFilterTypeInterface.php +++ b/src/FilterType/InitialFilterTypeInterface.php @@ -10,5 +10,5 @@ interface InitialFilterTypeInterface { - public function getInitialPalette(); + public function getInitialPalette(string $prependPalette, string $appendPalette); } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index 02435681..e5519f76 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -12,7 +12,7 @@ class ButtonType extends AbstractFilterType { - const TYPE = 'future_button'; + const TYPE = 'button_type'; public static function getType(): string { @@ -29,8 +29,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(string $prependPalette, string $appendPalette): string { - return parent::getPalette(); + return parent::getPalette($prependPalette, $appendPalette); } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index e4753ab3..fe41c910 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -12,7 +12,7 @@ class ChoiceType extends AbstractFilterType { - const TYPE = 'future_choice'; + const TYPE = 'choice_type'; public static function getType(): string { @@ -29,8 +29,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(string $prependPalette, string $appendPalette): string { - return parent::getPalette(); + return parent::getPalette($prependPalette, $appendPalette); } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 27dafc8c..30413b80 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -12,7 +12,12 @@ class DateTimeType extends AbstractFilterType { - const TYPE = 'future_date_time'; + const TYPE = 'date_time_type'; + + public static function test(): string + { + return 'test'; + } public static function getType(): string { @@ -29,8 +34,8 @@ public function buildForm($filterTypeContext) // TODO: Implement buildForm() method. } - public function getPalette(): string + public function getPalette(string $prependPalette, string $appendPalette): string { - return parent::getPalette(); + return parent::getPalette($prependPalette, $appendPalette); } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 5d456fe7..827ab6a4 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -8,33 +8,15 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; -use Doctrine\ORM\EntityManagerInterface; -use HeimrichHannot\FilterBundle\Filter\Filter; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; class TextType extends AbstractFilterType implements InitialFilterTypeInterface { - const TYPE = 'future_text'; + const TYPE = 'text_type'; const GROUP = 'text'; - /** - * @var EntityManagerInterface - */ - protected $em; - /** - * @var Filter - */ - protected $filter; - - public function __construct(Filter $filter, EntityManagerInterface $em) - { - $this->em = $em; - $this->filter = $filter; - $this->initialize(); - } - public static function getType(): string { return static::TYPE; @@ -57,22 +39,13 @@ public function buildForm(FilterTypeContext $filterTypeContext) { } - public function getPalette(): string - { - if ($this->getContext()->isInitial()) { - return $this->getInitialPalette(); - } - - return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;{expert_legend},cssClass;{publish_legend},published;'; - } - - public function getInitialPalette(): string + public function getPalette(string $prependPalette, string $appendPalette): string { - return '{initial_legend},isInitial;{general_legend},title,type;{config_legend},field;'; + return $prependPalette.'{config_legend},field;{expert_legend},cssClass;'.$appendPalette; } - private function initialize(): void + public function getInitialPalette(string $prependPalette, string $appendPalette): string { - $this->setGroup(static::GROUP); + return $prependPalette.'{config_legend},field;'.$appendPalette; } } diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index 12f1c396..dabbe834 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,7 +1,6 @@ huh: filter: disable_legacy_filters: true - initial_types: [ 'future_text' ] types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index d0336761..07ccc799 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -16,6 +16,7 @@ ['huh.filter.backend.filter_config_element', 'checkPermission'], ['huh.filter.backend.filter_config_element', 'modifyPalette'], ['huh.filter.backend.filter_config_element', 'prepareChoiceTypes'], + [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onLoadCallback'], ], 'onsubmit_callback' => [ ['huh.utils.dca', 'setDateAdded'], From 1a290791f82558b0e837a69f62a8907a957fb572 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 26 Feb 2021 17:38:29 +0100 Subject: [PATCH 06/58] fixed selecting of intial palette --- src/DataContainer/FilterConfigElementContainer.php | 1 + src/DependencyInjection/Configuration.php | 6 ------ src/FilterType/AbstractFilterType.php | 2 -- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 0f708348..c70b67ee 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -49,6 +49,7 @@ public function onLoadCallback(DataContainer $dc): void $type = $this->typeCollection->getType($model->type); if ($type instanceof InitialFilterTypeInterface && $model->isInitial) { + $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; $prependPalette = '{initial_legend},isInitial;{general_legend},title,type;'; $appendPalette = '{publish_legend},published;'; diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 332dff3e..58cf5abc 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -43,12 +43,6 @@ public function getConfigTreeBuilder() ->defaultFalse() ->info('Disable legacy filters to be able to use new implementation of filters together with HTTP GET requests.') ->end() - ->arrayNode('initial_types') - ->scalarPrototype() - ->defaultValue([]) - ->info('Filter types that can be used as initial.') - ->end() - ->end() ->arrayNode('types') ->arrayPrototype() ->children() diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 95427d04..44691df7 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -15,8 +15,6 @@ abstract class AbstractFilterType implements FilterTypeInterface { const GROUP_DEFAULT = 'miscellaneous'; - const PREPEND_PALETTE = 'type,name'; - /** * @var EntityManagerInterface */ From f891273f28bcaa3a69611f0dc7cbe142e85385d2 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 1 Apr 2021 17:36:31 +0200 Subject: [PATCH 07/58] added template, started orm filter query --- src/Config/FilterConfig.php | 25 ++++++++++++-- src/ContaoManager/Plugin.php | 1 + src/Controller/FrontendFilterController.php | 21 ++++++++++++ src/FilterType/FilterTypeContext.php | 36 +++++++++++++++++++++ src/FilterType/Type/TextType.php | 23 +++++++------ src/Form/FilterType.php | 34 +++++++++++++++++++ src/Resources/config/config.yml | 2 +- src/Resources/config/filter.yml | 4 +++ src/Resources/config/services.yml | 4 ++- 9 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 src/Controller/FrontendFilterController.php create mode 100644 src/Resources/config/filter.yml diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index c66312f3..f66fd077 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -16,6 +16,7 @@ use HeimrichHannot\FilterBundle\Filter\AbstractType; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; use HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension; use HeimrichHannot\FilterBundle\Form\FilterType; @@ -220,6 +221,9 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $types = $this->container->get('huh.filter.choice.type')->getCachedChoices(); + $newTypes = \System::getContainer()->get('huh.filter.filter_type.collection')->getTypes(); + $types = array_merge($types, $newTypes); + if (!\is_array($types) || empty($types)) { return; } @@ -230,8 +234,8 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip } // if ($type instanceof AbstractFilterType) { -// $filterContext = new FilterTypeContext($element, $this->filterConfig); -// $typ->buildQuery($queryBuilder, $filterContext); +// $filterContext = new FilterTypeContext(); +// $type->buildQuery($filterContext); // } if (!isset($types[$element->type]) || \in_array($element->id, $skipElements) || @@ -240,6 +244,12 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip continue; } + if (!\is_array($types[$element->type])) { + $this->processFilterType($element, $types[$element->type]); + + continue; + } + $config = $types[$element->type]; $class = $config['class']; $skip = $queryBuilder->getSkip(); @@ -617,6 +627,17 @@ public function jsonSerialize() return get_object_vars($this); } + protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filter) + { + $context = new FilterTypeContext(); + $context->setValue($config->value); + $context->setDefaultValue($config->defaultValue); + $context->setName($config->type.'_'.$config->id); + $context->setParent($config->pid); + + $filter->buildQuery($context); + } + protected function isResetButtonClicked(FormInterface $form): bool { if (!(null !== $form->getClickedButton() && \in_array($form->getClickedButton()->getName(), diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 4de219d9..cac51aaa 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -83,5 +83,6 @@ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInte public function registerContainerConfiguration(LoaderInterface $loader, array $managerConfig) { $loader->load('@HeimrichHannotContaoFilterBundle/Resources/config/datacontainer.yml'); + $loader->load('@HeimrichHannotContaoFilterBundle/Resources/config/filter.yml'); } } diff --git a/src/Controller/FrontendFilterController.php b/src/Controller/FrontendFilterController.php new file mode 100644 index 00000000..3140e016 --- /dev/null +++ b/src/Controller/FrontendFilterController.php @@ -0,0 +1,21 @@ +value)) { + return $this->getDefaultValue(); + } + return $this->value; } @@ -77,4 +93,24 @@ public function setInitial(): void { $this->initial = true; } + + public function getBuilder(): FormBuilderInterface + { + return $this->builder; + } + + public function setBuilder(FormBuilderInterface $builder): void + { + $this->builder = $builder; + } + + public function getParent(): string + { + return $this->parent; + } + + public function setParent(string $parent): void + { + $this->parent = $parent; + } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 827ab6a4..a440c2a8 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -11,6 +11,7 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use Symfony\Component\Form\Extension\Core\Type\TextType as SymfonyTextType; class TextType extends AbstractFilterType implements InitialFilterTypeInterface { @@ -24,19 +25,23 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { - try { - foreach ($filterTypeContext->getIterator() as $param) { - $this->filter->setParameter($param->key(), $param->current()); - } - } catch (\Exception $e) { - throw new \Exception($e->getMessage()); - } - - $this->em->getFilters()->enable('huh_filter'); +// try { +// foreach ($filterTypeContext->getIterator() as $param) { +// $this->filter->setParameter($param->key(), $param->current()); +// } +// } catch (\Exception $e) { +// throw new \Exception($e->getMessage()); +// } + + $filter = $this->em->getFilters()->enable('text_type'); +// $filter->setParameter(); } public function buildForm(FilterTypeContext $filterTypeContext) { + $builder = $filterTypeContext->getBuilder(); + + $builder->add($filterTypeContext->getName(), SymfonyTextType::class); } public function getPalette(string $prependPalette, string $appendPalette): string diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index d7bd8a87..50215143 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -13,6 +13,10 @@ use Contao\System; use HeimrichHannot\FilterBundle\Config\FilterConfig; use HeimrichHannot\FilterBundle\Exception\MissingFilterConfigException; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; +use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; @@ -113,6 +117,9 @@ protected function buildElements(FormBuilderInterface $builder, array $options) $wrappers = []; $types = \System::getContainer()->get('huh.filter.choice.type')->getCachedChoices(); + $newTypes = \System::getContainer()->get('huh.filter.filter_type.collection')->getTypes(); + $types = array_merge($types, $newTypes); + if (!\is_array($types) || empty($types)) { return; } @@ -126,6 +133,13 @@ protected function buildElements(FormBuilderInterface $builder, array $options) } $config = $types[$element->type]; + + if (!\is_array($config)) { + $this->buildFilterTypeElement($element, $config, $builder); + + continue; + } + $class = $config['class']; if (!class_exists($class)) { @@ -166,6 +180,26 @@ protected function buildElements(FormBuilderInterface $builder, array $options) $this->buildWrapperElements($wrappers, $builder, $options); } + protected function buildFilterTypeElement(FilterConfigElementModel $element, FilterTypeInterface $filterType, FormBuilderInterface $builder) + { + $context = new FilterTypeContext(); + + if ((bool) $element->isInitial) { + return; + } + + $context->setName($element->type.'_'.$element->id); + $context->setValue($element->value); + $context->setDefaultValue($element->defaultValue); + $context->setBuilder($builder); + + try { + $filterType->buildForm($context); + } catch (InvalidOptionException $e) { + return; + } + } + /** * Build the wrapper form elements. */ diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index dabbe834..d6fe020c 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -61,4 +61,4 @@ huh: - { value: DESC } - { value: ASC } classes: - - { name: basic, class: HeimrichHannot\FilterBundle\Sort\BasicSort } + - { name: basic, class: HeimrichHannot\FilterBundle\Sort\BasicSort } \ No newline at end of file diff --git a/src/Resources/config/filter.yml b/src/Resources/config/filter.yml new file mode 100644 index 00000000..2709c42d --- /dev/null +++ b/src/Resources/config/filter.yml @@ -0,0 +1,4 @@ +doctrine: + orm: + filters: + text_type: HeimrichHannot\FilterBundle\Filter\Filter \ No newline at end of file diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index c997df66..6a808eb9 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -182,4 +182,6 @@ services: HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection: arguments: - - !tagged huh.filter.filter_type \ No newline at end of file + - !tagged huh.filter.filter_type + + huh.filter.filter_type.collection: '@HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection' From a2605e6600a92b38611d826be217948ba7d3b37d Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Wed, 7 Apr 2021 17:36:56 +0200 Subject: [PATCH 08/58] working on querybuilder --- src/Config/FilterConfig.php | 21 ++++++++++-- src/Filter/FilterQueryPart.php | 22 ++++++++++++ src/Filter/FilterQueryPartProcessor.php | 43 +++++++++++++++++++++++ src/FilterType/FilterTypeContext.php | 45 +++++++++++++++++++------ src/FilterType/Type/ButtonType.php | 3 +- src/FilterType/Type/ChoiceType.php | 3 +- src/FilterType/Type/DateTimeType.php | 3 +- src/FilterType/Type/TextType.php | 14 ++------ src/Form/FilterType.php | 3 +- src/Resources/config/services.yml | 2 ++ 10 files changed, 130 insertions(+), 29 deletions(-) create mode 100644 src/Filter/FilterQueryPart.php create mode 100644 src/Filter/FilterQueryPartProcessor.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index f66fd077..7ee94817 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -9,12 +9,13 @@ namespace HeimrichHannot\FilterBundle\Config; use Contao\Controller; +use Contao\CoreBundle\Doctrine\Schema\DcaSchemaProvider; use Contao\CoreBundle\Framework\ContaoFrameworkInterface; use Contao\Environment; use Contao\InsertTags; use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; use HeimrichHannot\FilterBundle\Filter\AbstractType; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; @@ -92,6 +93,14 @@ class FilterConfig implements \JsonSerializable * @var bool */ protected $formSubmitted = false; + /** + * @var DcaSchemaProvider + */ + protected $schemaProvider; + /** + * @var EntityManagerInterface + */ + protected EntityManagerInterface $em; /** * @var ContainerInterface */ @@ -110,13 +119,17 @@ public function __construct( ContaoFrameworkInterface $framework, FilterSession $session, Connection $connection, - RequestStack $requestStack + RequestStack $requestStack, + DcaSchemaProvider $schemaProvider, + EntityManagerInterface $em ) { $this->framework = $framework; $this->session = $session; $this->container = $container; $this->queryBuilder = new FilterQueryBuilder($this->container, $this->framework, $connection); $this->requestStack = $requestStack; + $this->schemaProvider = $schemaProvider; + $this->em = $em; } /** @@ -633,7 +646,9 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setValue($config->value); $context->setDefaultValue($config->defaultValue); $context->setName($config->type.'_'.$config->id); - $context->setParent($config->pid); + $context->setParent($config->getRelated('pid')); + + $context->setQueryBuilder($this->queryBuilder); $filter->buildQuery($context); } diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php new file mode 100644 index 00000000..5ec7109b --- /dev/null +++ b/src/Filter/FilterQueryPart.php @@ -0,0 +1,22 @@ +parts; + } + + /** + * @param FilterQueryPart[] $parts + */ + public function setParts(array $parts): void + { + $this->parts = $parts; + } + + public function addPart(FilterQueryPart $part): void + { + $this->parts[$part->name] = $part; + } + + public function removePart(string $name): void + { + unset($name, $this->parts); + } +} diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 0f7d5981..aecd50ae 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -8,6 +8,8 @@ namespace HeimrichHannot\FilterBundle\FilterType; +use Contao\Model; +use Doctrine\DBAL\Query\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class FilterTypeContext implements \IteratorAggregate @@ -28,12 +30,17 @@ class FilterTypeContext implements \IteratorAggregate /** * @var FormBuilderInterface */ - private $builder; + private $formBuilder; /** - * @var string + * @var QueryBuilder */ - private $parent = ''; + private $queryBuilder; + + /** + * @var Model + */ + private $parent = null; /** * @var bool @@ -94,23 +101,39 @@ public function setInitial(): void $this->initial = true; } - public function getBuilder(): FormBuilderInterface + /** + * @return Model + */ + public function getParent(): ?Model + { + return $this->parent; + } + + /** + * @param Model $parent + */ + public function setParent(?Model $parent): void { - return $this->builder; + $this->parent = $parent; } - public function setBuilder(FormBuilderInterface $builder): void + public function getQueryBuilder(): QueryBuilder { - $this->builder = $builder; + return $this->queryBuilder; } - public function getParent(): string + public function setQueryBuilder(QueryBuilder $queryBuilder): void { - return $this->parent; + $this->queryBuilder = $queryBuilder; } - public function setParent(string $parent): void + public function getFormBuilder(): FormBuilderInterface { - $this->parent = $parent; + return $this->formBuilder; + } + + public function setFormBuilder(FormBuilderInterface $formBuilder): void + { + $this->formBuilder = $formBuilder; } } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index e5519f76..d3bb1543 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ButtonType extends AbstractFilterType { @@ -19,7 +20,7 @@ public static function getType(): string return static::TYPE; } - public function buildQuery($filterTypeContext): string + public function buildQuery(FilterTypeContext $filterTypeContext): string { // TODO: Implement buildQuery() method. } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index fe41c910..c1219316 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class ChoiceType extends AbstractFilterType { @@ -19,7 +20,7 @@ public static function getType(): string return static::TYPE; } - public function buildQuery($filterTypeContext): string + public function buildQuery(FilterTypeContext $filterTypeContext): string { // TODO: Implement buildQuery() method. } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 30413b80..df492150 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class DateTimeType extends AbstractFilterType { @@ -24,7 +25,7 @@ public static function getType(): string return static::TYPE; } - public function buildQuery($filterTypeContext): string + public function buildQuery(FilterTypeContext $filterTypeContext): string { // TODO: Implement buildQuery() method. } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index a440c2a8..da07ce7a 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -25,21 +25,13 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { -// try { -// foreach ($filterTypeContext->getIterator() as $param) { -// $this->filter->setParameter($param->key(), $param->current()); -// } -// } catch (\Exception $e) { -// throw new \Exception($e->getMessage()); -// } - - $filter = $this->em->getFilters()->enable('text_type'); -// $filter->setParameter(); + $queryBuilder = $filterTypeContext->getQueryBuilder(); + $queryBuilder->whereElement(); } public function buildForm(FilterTypeContext $filterTypeContext) { - $builder = $filterTypeContext->getBuilder(); + $builder = $filterTypeContext->getFormBuilder(); $builder->add($filterTypeContext->getName(), SymfonyTextType::class); } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 50215143..c7c18554 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -191,7 +191,8 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setName($element->type.'_'.$element->id); $context->setValue($element->value); $context->setDefaultValue($element->defaultValue); - $context->setBuilder($builder); + + $context->setFormBuilder($builder); try { $filterType->buildForm($context); diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 6a808eb9..ef9f96dc 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -45,6 +45,8 @@ services: - "@huh.filter.session" - "@doctrine.dbal.default_connection" - "@request_stack" + - "@contao.doctrine.schema_provider" + - "@doctrine.orm.entity_manager" huh.filter.backend.filter_config_element: public: true From ef3e2345314e19d27d0d98a7fc768eaed62e0d4d Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 12 Apr 2021 17:59:17 +0200 Subject: [PATCH 09/58] added querybuilder where for TextFilterType --- src/Config/FilterConfig.php | 21 ++++++++--- src/FilterType/FilterTypeContext.php | 52 +++++++++++++++++++++++++--- src/FilterType/Type/TextType.php | 11 +++++- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 7ee94817..f2fd6a82 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -25,6 +25,7 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigModel; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use HeimrichHannot\FilterBundle\Session\FilterSession; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\FormBuilderInterface; @@ -100,7 +101,8 @@ class FilterConfig implements \JsonSerializable /** * @var EntityManagerInterface */ - protected EntityManagerInterface $em; + protected $em; + protected $databaseUtil; /** * @var ContainerInterface */ @@ -121,7 +123,8 @@ public function __construct( Connection $connection, RequestStack $requestStack, DcaSchemaProvider $schemaProvider, - EntityManagerInterface $em + EntityManagerInterface $em, + DatabaseUtil $databaseUtil ) { $this->framework = $framework; $this->session = $session; @@ -130,6 +133,7 @@ public function __construct( $this->requestStack = $requestStack; $this->schemaProvider = $schemaProvider; $this->em = $em; + $this->databaseUtil = $databaseUtil; } /** @@ -643,12 +647,19 @@ public function jsonSerialize() protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filter) { $context = new FilterTypeContext(); - $context->setValue($config->value); - $context->setDefaultValue($config->defaultValue); $context->setName($config->type.'_'.$config->id); - $context->setParent($config->getRelated('pid')); + $context->setField($config->field); + $context->setValue($this->getData()[$context->getName()]); + if (!empty($config->operator)) { + $context->setOperator($config->operator); + } else { + $context->setOperator(DatabaseUtil::OPERATOR_LIKE); + } + $context->setDefaultValue($config->defaultValue); + $context->setParent($config->getRelated('pid')); $context->setQueryBuilder($this->queryBuilder); + $context->setDatabaseUtil($this->databaseUtil); $filter->buildQuery($context); } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index aecd50ae..6de58cac 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -10,18 +10,27 @@ use Contao\Model; use Doctrine\DBAL\Query\QueryBuilder; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; class FilterTypeContext implements \IteratorAggregate { /** - * @var string + * @var DatabaseUtil */ - private $name = ''; + private $databaseUtil; /** * @var string */ private $defaultValue = ''; + /** + * @var string + */ + private $field = ''; + /** + * @var string + */ + private $name = ''; /** * @var string */ @@ -33,14 +42,17 @@ class FilterTypeContext implements \IteratorAggregate private $formBuilder; /** - * @var QueryBuilder + * @var string */ - private $queryBuilder; - + private $operator; /** * @var Model */ private $parent = null; + /** + * @var QueryBuilder + */ + private $queryBuilder; /** * @var bool @@ -136,4 +148,34 @@ public function setFormBuilder(FormBuilderInterface $formBuilder): void { $this->formBuilder = $formBuilder; } + + public function getDatabaseUtil(): DatabaseUtil + { + return $this->databaseUtil; + } + + public function setDatabaseUtil(DatabaseUtil $databaseUtil): void + { + $this->databaseUtil = $databaseUtil; + } + + public function getField(): string + { + return $this->field; + } + + public function setField(string $field): void + { + $this->field = $field; + } + + public function getOperator(): string + { + return $this->operator; + } + + public function setOperator(string $operator): void + { + $this->operator = $operator; + } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index da07ce7a..786d70d5 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -26,7 +26,16 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { $queryBuilder = $filterTypeContext->getQueryBuilder(); - $queryBuilder->whereElement(); + $where = $filterTypeContext->getDatabaseUtil()->composeWhereForQueryBuilder( + $filterTypeContext->getQueryBuilder(), + $filterTypeContext->getField(), + $filterTypeContext->getOperator(), + null, + $filterTypeContext->getValue() + ); + + $queryBuilder->andWhere($where); +// $queryBuilder->setParameter() } public function buildForm(FilterTypeContext $filterTypeContext) From 4730781269b36b08e040578295b9e2350e8c5b8b Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 13 Apr 2021 15:54:51 +0200 Subject: [PATCH 10/58] added processing of queryParts --- src/Config/FilterConfig.php | 44 ++++++++++++----------- src/Event/ModifyFilterQueryPartsEvent.php | 36 +++++++++++++++++++ src/Filter/FilterQueryPart.php | 26 ++++++++++++++ src/Filter/FilterQueryPartCollection.php | 33 +++++++++++++++++ src/Filter/FilterQueryPartProcessor.php | 31 +++++----------- src/FilterType/AbstractFilterType.php | 19 +++++----- src/FilterType/FilterTypeContext.php | 30 ++++++++-------- src/FilterType/Type/TextType.php | 14 ++------ src/Resources/config/services.yml | 9 +++-- 9 files changed, 162 insertions(+), 80 deletions(-) create mode 100644 src/Event/ModifyFilterQueryPartsEvent.php create mode 100644 src/Filter/FilterQueryPartCollection.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index f2fd6a82..faa70e85 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -9,13 +9,14 @@ namespace HeimrichHannot\FilterBundle\Config; use Contao\Controller; -use Contao\CoreBundle\Doctrine\Schema\DcaSchemaProvider; use Contao\CoreBundle\Framework\ContaoFrameworkInterface; use Contao\Environment; use Contao\InsertTags; use Doctrine\DBAL\Connection; -use Doctrine\ORM\EntityManagerInterface; +use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; @@ -27,6 +28,7 @@ use HeimrichHannot\FilterBundle\Session\FilterSession; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; @@ -94,15 +96,13 @@ class FilterConfig implements \JsonSerializable * @var bool */ protected $formSubmitted = false; + /** - * @var DcaSchemaProvider - */ - protected $schemaProvider; - /** - * @var EntityManagerInterface + * @var FilterQueryPartCollection */ - protected $em; - protected $databaseUtil; + protected $filterQueryPartCollection; + protected EventDispatcherInterface $eventDispatcher; + /** * @var ContainerInterface */ @@ -122,18 +122,16 @@ public function __construct( FilterSession $session, Connection $connection, RequestStack $requestStack, - DcaSchemaProvider $schemaProvider, - EntityManagerInterface $em, - DatabaseUtil $databaseUtil + FilterQueryPartCollection $filterQueryPartCollection, + EventDispatcherInterface $eventDispatcher ) { $this->framework = $framework; $this->session = $session; $this->container = $container; $this->queryBuilder = new FilterQueryBuilder($this->container, $this->framework, $connection); $this->requestStack = $requestStack; - $this->schemaProvider = $schemaProvider; - $this->em = $em; - $this->databaseUtil = $databaseUtil; + $this->filterQueryPartCollection = $filterQueryPartCollection; + $this->eventDispatcher = $eventDispatcher; } /** @@ -250,10 +248,9 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip return; } -// if ($type instanceof AbstractFilterType) { -// $filterContext = new FilterTypeContext(); -// $type->buildQuery($filterContext); -// } + if ($types[$element->type] instanceof AbstractFilterType) { + $this->processFilterType($element, $types[$element->type]); + } if (!isset($types[$element->type]) || \in_array($element->id, $skipElements) || $mode === static::QUERY_BUILDER_MODE_INITIAL_ONLY && !$element->isInitial || @@ -284,6 +281,13 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $type->buildQuery($queryBuilder, $element); } + + //apply parts from FilterQueryPartCollection + $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection)); + + foreach ($event->getPartsCollection()->getParts() as $part) { + $this->queryBuilder->andWhere($part->query); + } } /** @@ -647,6 +651,7 @@ public function jsonSerialize() protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filter) { $context = new FilterTypeContext(); + $context->setId($config->id); $context->setName($config->type.'_'.$config->id); $context->setField($config->field); $context->setValue($this->getData()[$context->getName()]); @@ -659,7 +664,6 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setDefaultValue($config->defaultValue); $context->setParent($config->getRelated('pid')); $context->setQueryBuilder($this->queryBuilder); - $context->setDatabaseUtil($this->databaseUtil); $filter->buildQuery($context); } diff --git a/src/Event/ModifyFilterQueryPartsEvent.php b/src/Event/ModifyFilterQueryPartsEvent.php new file mode 100644 index 00000000..07853b75 --- /dev/null +++ b/src/Event/ModifyFilterQueryPartsEvent.php @@ -0,0 +1,36 @@ +partsCollection = $partsCollection; + } + + public function getPartsCollection(): FilterQueryPartCollection + { + return $this->partsCollection; + } + + public function setPartsCollection(FilterQueryPartCollection $partsCollection): void + { + $this->partsCollection = $partsCollection; + } +} diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php index 5ec7109b..fd2dc0a6 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/Filter/FilterQueryPart.php @@ -8,6 +8,9 @@ namespace HeimrichHannot\FilterBundle\Filter; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; + class FilterQueryPart { /** @@ -19,4 +22,27 @@ class FilterQueryPart * @var string */ public string $query; + /** + * @var DatabaseUtil + */ + protected $databaseUtil; + + public function __construct(FilterTypeContext $context, DatabaseUtil $databaseUtil) + { + $this->databaseUtil = $databaseUtil; + $this->name = $context->getName(); + $this->query = $this->composeQuery($context); + } + + private function composeQuery(FilterTypeContext $context): string + { + return $this->databaseUtil->composeWhereForQueryBuilder( + $context->getQueryBuilder(), + $context->getField(), + $context->getOperator(), + null, + $context->getValue(), + ['wildcardSuffix' => $context->getId()] + ); + } } diff --git a/src/Filter/FilterQueryPartCollection.php b/src/Filter/FilterQueryPartCollection.php new file mode 100644 index 00000000..11df09c6 --- /dev/null +++ b/src/Filter/FilterQueryPartCollection.php @@ -0,0 +1,33 @@ +parts; + } + + public function addPart(FilterQueryPart $part): void + { + $this->parts[$part->name] = $part; + } + + public function removePartByName(string $name): void + { + unset($this->parts[$name]); + } + +} \ No newline at end of file diff --git a/src/Filter/FilterQueryPartProcessor.php b/src/Filter/FilterQueryPartProcessor.php index ce04a2c2..2db585a0 100644 --- a/src/Filter/FilterQueryPartProcessor.php +++ b/src/Filter/FilterQueryPartProcessor.php @@ -8,36 +8,23 @@ namespace HeimrichHannot\FilterBundle\Filter; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; + class FilterQueryPartProcessor { /** - * @var FilterQueryPart[] - */ - private array $parts = []; - - /** - * @return FilterQueryPart[] + * @var DatabaseUtil */ - public function getParts(): array - { - return $this->parts; - } - - /** - * @param FilterQueryPart[] $parts - */ - public function setParts(array $parts): void - { - $this->parts = $parts; - } + protected $databaseUtil; - public function addPart(FilterQueryPart $part): void + public function __construct(DatabaseUtil $databaseUtil) { - $this->parts[$part->name] = $part; + $this->databaseUtil = $databaseUtil; } - public function removePart(string $name): void + public function composeQueryPart(FilterTypeContext $context): FilterQueryPart { - unset($name, $this->parts); + return new FilterQueryPart($context, $this->databaseUtil); } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 44691df7..7706d55e 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -8,21 +8,22 @@ namespace HeimrichHannot\FilterBundle\FilterType; -use Doctrine\ORM\EntityManagerInterface; -use HeimrichHannot\FilterBundle\Filter\Filter; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; abstract class AbstractFilterType implements FilterTypeInterface { const GROUP_DEFAULT = 'miscellaneous'; /** - * @var EntityManagerInterface + * @var FilterQueryPartProcessor */ - protected $em; + protected $filterQueryPartProcessor; + /** - * @var Filter + * @var FilterQueryPartCollection */ - protected $filter; + protected $filterQueryPartCollection; /** * @var FilterTypeContext @@ -34,11 +35,11 @@ abstract class AbstractFilterType implements FilterTypeInterface */ private $group = ''; - public function __construct(Filter $filter, EntityManagerInterface $em) + public function __construct(FilterQueryPartProcessor $filterQueryPartProcessor, FilterQueryPartCollection $filterQueryPartCollection) { - $this->em = $em; - $this->filter = $filter; $this->initialize(); + $this->filterQueryPartProcessor = $filterQueryPartProcessor; + $this->filterQueryPartCollection = $filterQueryPartCollection; } public function getContext(): FilterTypeContext diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 6de58cac..b4a209d2 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -10,15 +10,10 @@ use Contao\Model; use Doctrine\DBAL\Query\QueryBuilder; -use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; class FilterTypeContext implements \IteratorAggregate { - /** - * @var DatabaseUtil - */ - private $databaseUtil; /** * @var string */ @@ -54,6 +49,11 @@ class FilterTypeContext implements \IteratorAggregate */ private $queryBuilder; + /** + * @var int + */ + private $id; + /** * @var bool */ @@ -149,16 +149,6 @@ public function setFormBuilder(FormBuilderInterface $formBuilder): void $this->formBuilder = $formBuilder; } - public function getDatabaseUtil(): DatabaseUtil - { - return $this->databaseUtil; - } - - public function setDatabaseUtil(DatabaseUtil $databaseUtil): void - { - $this->databaseUtil = $databaseUtil; - } - public function getField(): string { return $this->field; @@ -178,4 +168,14 @@ public function setOperator(string $operator): void { $this->operator = $operator; } + + public function getId(): int + { + return $this->id; + } + + public function setId(int $id): void + { + $this->id = $id; + } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 786d70d5..d5d32aa9 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -25,17 +25,7 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { - $queryBuilder = $filterTypeContext->getQueryBuilder(); - $where = $filterTypeContext->getDatabaseUtil()->composeWhereForQueryBuilder( - $filterTypeContext->getQueryBuilder(), - $filterTypeContext->getField(), - $filterTypeContext->getOperator(), - null, - $filterTypeContext->getValue() - ); - - $queryBuilder->andWhere($where); -// $queryBuilder->setParameter() + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } public function buildForm(FilterTypeContext $filterTypeContext) @@ -52,6 +42,6 @@ public function getPalette(string $prependPalette, string $appendPalette): strin public function getInitialPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator;'.$appendPalette; } } diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index ef9f96dc..5dd4b63c 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -45,8 +45,6 @@ services: - "@huh.filter.session" - "@doctrine.dbal.default_connection" - "@request_stack" - - "@contao.doctrine.schema_provider" - - "@doctrine.orm.entity_manager" huh.filter.backend.filter_config_element: public: true @@ -187,3 +185,10 @@ services: - !tagged huh.filter.filter_type huh.filter.filter_type.collection: '@HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection' + + HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection: + + huh.filter.filter_query_part_collection: '@HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection' + + HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor: + huh.filter.filter_query_part_processor: '@HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor' From fbf0997b308909c7b81b3656d8a6cbee37ad453e Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 15 Apr 2021 14:27:57 +0200 Subject: [PATCH 11/58] text-type working --- src/Config/FilterConfig.php | 12 +- .../FilterConfigElementContainer.php | 10 ++ src/Filter/FilterQueryPart.php | 18 ++- src/FilterType/AbstractFilterType.php | 46 ++++++- src/FilterType/FilterTypeContext.php | 129 +++++++++++++----- src/FilterType/Type/ChoiceType.php | 27 +++- src/FilterType/Type/TextType.php | 20 ++- src/Form/FilterType.php | 10 +- .../contao/dca/tl_filter_config_element.php | 2 +- 9 files changed, 219 insertions(+), 55 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index faa70e85..67b3fb9f 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -26,7 +26,6 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigModel; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use HeimrichHannot\FilterBundle\Session\FilterSession; -use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Exception\TransformationFailedException; @@ -654,16 +653,11 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setId($config->id); $context->setName($config->type.'_'.$config->id); $context->setField($config->field); - $context->setValue($this->getData()[$context->getName()]); - - if (!empty($config->operator)) { - $context->setOperator($config->operator); - } else { - $context->setOperator(DatabaseUtil::OPERATOR_LIKE); - } + $context->setOperator($config->operator); + $context->setValue($this->getData()[$context->getName()] ?: ''); $context->setDefaultValue($config->defaultValue); - $context->setParent($config->getRelated('pid')); $context->setQueryBuilder($this->queryBuilder); + $context->setParent($config->getRelated('pid')); $filter->buildQuery($context); } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index c70b67ee..cf985010 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -14,6 +14,7 @@ use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; class FilterConfigElementContainer { @@ -78,4 +79,13 @@ public function getTypeOptions(DataContainer $dc) return $options; } + + public function getOperators(DataContainer $dc) + { + if (!$this->bundleConfig['filter']['disable_legacy_filters']) { + return DatabaseUtil::OPERATORS; + } + + return $this->typeCollection->getType($dc->activeRecord->type)->getOperators(); + } } diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php index fd2dc0a6..8ef828d8 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/Filter/FilterQueryPart.php @@ -21,19 +21,35 @@ class FilterQueryPart /** * @var string */ - public string $query; + public $query; /** * @var DatabaseUtil */ protected $databaseUtil; + /** + * @var int + */ + protected $filterElementId; + public function __construct(FilterTypeContext $context, DatabaseUtil $databaseUtil) { $this->databaseUtil = $databaseUtil; $this->name = $context->getName(); + $this->filterElementId = $context->getId(); $this->query = $this->composeQuery($context); } + public function getFilterElementId(): int + { + return $this->filterElementId; + } + + public function setFilterElementId(int $filterElementId): void + { + $this->filterElementId = $filterElementId; + } + private function composeQuery(FilterTypeContext $context): string { return $this->databaseUtil->composeWhereForQueryBuilder( diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 7706d55e..b8024816 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -10,6 +10,8 @@ use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use Symfony\Contracts\Translation\TranslatorInterface; abstract class AbstractFilterType implements FilterTypeInterface { @@ -25,6 +27,11 @@ abstract class AbstractFilterType implements FilterTypeInterface */ protected $filterQueryPartCollection; + /** + * @var TranslatorInterface + */ + protected $translator; + /** * @var FilterTypeContext */ @@ -35,11 +42,15 @@ abstract class AbstractFilterType implements FilterTypeInterface */ private $group = ''; - public function __construct(FilterQueryPartProcessor $filterQueryPartProcessor, FilterQueryPartCollection $filterQueryPartCollection) - { + public function __construct( + FilterQueryPartProcessor $filterQueryPartProcessor, + FilterQueryPartCollection $filterQueryPartCollection, + TranslatorInterface $translator + ) { $this->initialize(); $this->filterQueryPartProcessor = $filterQueryPartProcessor; $this->filterQueryPartCollection = $filterQueryPartCollection; + $this->translator = $translator; } public function getContext(): FilterTypeContext @@ -71,6 +82,37 @@ public function setGroup(string $group): void $this->group = $group; } + public function getOperators(): array + { + return DatabaseUtil::OPERATORS; + } + + public function buildQuery(FilterTypeContext $filterTypeContext) + { + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); + } + + public function getOptions(FilterTypeContext $context): array + { + $options = []; + + if ('' !== $context->getPlaceholder()) { + $options['attr']['placeholder'] = $this->translator->trans($context->getPlaceholder(), ['%label%' => $this->translator->trans($options['label']) ?: $context->getTitle()]); + } + + $options['label'] = $context->getLabel() ?: $context->getTitle(); + + // sr-only style for non-bootstrap projects is shipped within the filter_form_* templates + if (true === $context->isLabelHidden()) { + $options['label_attr'] = ['class' => 'sr-only']; + } + + // always label for screen readers + $options['attr']['aria-label'] = $this->translator->trans($context->getLabel() ?: $context->getTitle()); + + return $options; + } + protected function initialize(): void { if (empty($this->group) && !\defined('static::GROUP')) { diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index b4a209d2..5c1e9963 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -22,42 +22,63 @@ class FilterTypeContext implements \IteratorAggregate * @var string */ private $field = ''; + /** - * @var string + * @var FormBuilderInterface */ - private $name = ''; + private $formBuilder; + + /** + * @var int + */ + private $id; + + /** + * @var bool + */ + private $initial = false; + /** * @var string */ - private $value = ''; + private $label = ''; /** - * @var FormBuilderInterface + * @var bool */ - private $formBuilder; + private $isLabelHidden = false; /** * @var string */ - private $operator; + private $name = ''; + /** + * @var string + */ + private $operator = ''; /** * @var Model */ private $parent = null; + + /** + * string. + */ + private $placeholder = null; + /** * @var QueryBuilder */ private $queryBuilder; - /** - * @var int + * @var string */ - private $id; + private $title; /** - * @var bool + * @var string */ - private $initial = false; + private $value = ''; public function getName(): string { @@ -69,16 +90,6 @@ public function setName(string $name): void $this->name = $name; } - public function getDefaultValue(): string - { - return $this->defaultValue; - } - - public function setDefaultValue(string $defaultValue): void - { - $this->defaultValue = $defaultValue; - } - public function getValue(): string { if (empty($this->value)) { @@ -93,6 +104,16 @@ public function setValue(string $value): void $this->value = $value; } + public function getDefaultValue(): string + { + return $this->defaultValue; + } + + public function setDefaultValue(string $defaultValue): void + { + $this->defaultValue = $defaultValue; + } + public function getContext(): self { return $this; @@ -129,16 +150,6 @@ public function setParent(?Model $parent): void $this->parent = $parent; } - public function getQueryBuilder(): QueryBuilder - { - return $this->queryBuilder; - } - - public function setQueryBuilder(QueryBuilder $queryBuilder): void - { - $this->queryBuilder = $queryBuilder; - } - public function getFormBuilder(): FormBuilderInterface { return $this->formBuilder; @@ -178,4 +189,60 @@ public function setId(int $id): void { $this->id = $id; } + + /** + * @return null + */ + public function getPlaceholder() + { + return $this->placeholder; + } + + /** + * @param null $placeholder + */ + public function setPlaceholder($placeholder): void + { + $this->placeholder = $placeholder; + } + + public function getTitle(): string + { + return $this->title; + } + + public function setTitle(string $title): void + { + $this->title = $title; + } + + public function getLabel(): string + { + return $this->label; + } + + public function setLabel(string $label): void + { + $this->label = $label; + } + + public function isLabelHidden(): bool + { + return $this->isLabelHidden; + } + + public function hideLabel(): void + { + $this->isLabelHidden = true; + } + + public function getQueryBuilder(): QueryBuilder + { + return $this->queryBuilder; + } + + public function setQueryBuilder(QueryBuilder $queryBuilder): void + { + $this->queryBuilder = $queryBuilder; + } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index c1219316..a0ad45f7 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -9,7 +9,8 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; class ChoiceType extends AbstractFilterType { @@ -20,18 +21,30 @@ public static function getType(): string return static::TYPE; } - public function buildQuery(FilterTypeContext $filterTypeContext): string + public function buildForm($filterTypeContext) { - // TODO: Implement buildQuery() method. + $builder = $filterTypeContext->getFormBuilder(); + + $builder->add($filterTypeContext->getName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); } - public function buildForm($filterTypeContext) + public function getPalette(string $prependPalette, string $appendPalette): string { - // TODO: Implement buildForm() method. + return $prependPalette.'{config_legend},field,operator;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; } - public function getPalette(string $prependPalette, string $appendPalette): string + public function getOperators(): array { - return parent::getPalette($prependPalette, $appendPalette); + //remove this operators from the DatabaseUtil::OPERATORS array + $remove = [ + DatabaseUtil::OPERATOR_LIKE, + DatabaseUtil::OPERATOR_UNLIKE, + DatabaseUtil::OPERATOR_GREATER, + DatabaseUtil::OPERATOR_GREATER_EQUAL, + DatabaseUtil::OPERATOR_LOWER, + DatabaseUtil::OPERATOR_LOWER_EQUAL, + ]; + + return array_values(array_diff(parent::getOperators(), $remove)); } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index d5d32aa9..19567118 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -11,6 +11,7 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\Extension\Core\Type\TextType as SymfonyTextType; class TextType extends AbstractFilterType implements InitialFilterTypeInterface @@ -32,16 +33,31 @@ public function buildForm(FilterTypeContext $filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - $builder->add($filterTypeContext->getName(), SymfonyTextType::class); + $builder->add($filterTypeContext->getName(), SymfonyTextType::class, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field;{expert_legend},cssClass;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; } public function getInitialPalette(string $prependPalette, string $appendPalette): string { return $prependPalette.'{config_legend},field,operator;'.$appendPalette; } + + public function getOperators(): array + { + //remove this operators from the DatabaseUtil::OPERATORS array + $remove = [ + DatabaseUtil::OPERATOR_GREATER, + DatabaseUtil::OPERATOR_GREATER_EQUAL, + DatabaseUtil::OPERATOR_LOWER, + DatabaseUtil::OPERATOR_LOWER_EQUAL, + DatabaseUtil::OPERATOR_IN, + DatabaseUtil::OPERATOR_NOT_IN, + ]; + + return array_values(array_diff(parent::getOperators(), $remove)); + } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index c7c18554..4aac0e98 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -184,15 +184,21 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil { $context = new FilterTypeContext(); - if ((bool) $element->isInitial) { + if ($element->isInitial) { return; } $context->setName($element->type.'_'.$element->id); $context->setValue($element->value); $context->setDefaultValue($element->defaultValue); - + $context->setPlaceholder($element->placeholder); $context->setFormBuilder($builder); + $context->setTitle($element->title); + $context->setLabel($element->label); + + if ($element->hideLabel) { + $context->hideLabel(); + } try { $filterType->buildForm($context); diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 07ccc799..9e353820 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -357,7 +357,7 @@ 'operator' => [ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['operator'], 'inputType' => 'select', - 'options' => \HeimrichHannot\UtilsBundle\Database\DatabaseUtil::OPERATORS, + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getOperators'], 'reference' => &$GLOBALS['TL_LANG']['MSC']['databaseOperators'], 'eval' => ['tl_class' => 'w50', 'mandatory' => true, 'includeBlankOption' => true], 'sql' => "varchar(16) NOT NULL default ''", From ae8d55c32567ca337bfd8d63977eb3497eb37f70 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 16 Apr 2021 13:57:22 +0200 Subject: [PATCH 12/58] added basic choice_type --- src/Config/FilterConfig.php | 1 + src/FilterType/FilterTypeContext.php | 16 +++++++ src/FilterType/Type/ChoiceType.php | 65 +++++++++++++++++++++++++++- src/Form/FilterType.php | 3 ++ src/Resources/config/services.yml | 1 + 5 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 67b3fb9f..099f470a 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -658,6 +658,7 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setDefaultValue($config->defaultValue); $context->setQueryBuilder($this->queryBuilder); $context->setParent($config->getRelated('pid')); + $context->setSubmitOnChange($config->submitOnChange); $filter->buildQuery($context); } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 5c1e9963..03800de7 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -70,6 +70,12 @@ class FilterTypeContext implements \IteratorAggregate * @var QueryBuilder */ private $queryBuilder; + + /** + * @var bool + */ + private $submitOnChange = false; + /** * @var string */ @@ -245,4 +251,14 @@ public function setQueryBuilder(QueryBuilder $queryBuilder): void { $this->queryBuilder = $queryBuilder; } + + public function isSubmitOnChange(): bool + { + return $this->submitOnChange; + } + + public function setSubmitOnChange(bool $submitOnChange): void + { + $this->submitOnChange = $submitOnChange; + } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index a0ad45f7..0d18459e 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -8,13 +8,37 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; +use Doctrine\DBAL\Driver\Connection; +use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; +use Symfony\Contracts\Translation\TranslatorInterface; class ChoiceType extends AbstractFilterType { const TYPE = 'choice_type'; + protected FieldOptionsChoice $fieldOptionsChoice; + protected ModelUtil $modelUtil; + protected Connection $connection; + + public function __construct( + FilterQueryPartProcessor $filterQueryPartProcessor, + FilterQueryPartCollection $filterQueryPartCollection, + TranslatorInterface $translator, + FieldOptionsChoice $fieldOptionsChoice, + ModelUtil $modelUtil, + Connection $connection + ) { + parent::__construct($filterQueryPartProcessor, $filterQueryPartCollection, $translator); + $this->fieldOptionsChoice = $fieldOptionsChoice; + $this->modelUtil = $modelUtil; + $this->connection = $connection; + } public static function getType(): string { @@ -30,13 +54,15 @@ public function buildForm($filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,submitOnChange;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; } public function getOperators(): array { //remove this operators from the DatabaseUtil::OPERATORS array $remove = [ + DatabaseUtil::OPERATOR_EQUAL, + DatabaseUtil::OPERATOR_UNEQUAL, DatabaseUtil::OPERATOR_LIKE, DatabaseUtil::OPERATOR_UNLIKE, DatabaseUtil::OPERATOR_GREATER, @@ -47,4 +73,41 @@ public function getOperators(): array return array_values(array_diff(parent::getOperators(), $remove)); } + + public function getOptions(FilterTypeContext $filterTypeContext): array + { + $options = parent::getOptions($filterTypeContext); + $options['choices'] = array_flip($this->collectChoices($filterTypeContext)); + + if ($filterTypeContext->isSubmitOnChange()) { + if ($filterTypeContext->getParent()->asyncFormSubmit) { + $options['attr']['data-submit-on-change'] = 1; + } else { + if ($options['expanded']) { + $options['choice_attr'] = function ($choiceValue, $key, $value) { + return ['onchange' => 'this.form.submit()']; + }; + } else { + $options['attr']['onchange'] = 'this.form.submit()'; + } + } + } + + return $options; + } + + /** + * Get the list of available choices. + */ + public function collectChoices(FilterTypeContext $context): array + { + if (null === ($element = $this->modelUtil->findModelInstanceByPk('tl_filter_config_element', $context->getId()))) { + return []; + } + + return $this->fieldOptionsChoice->getCachedChoices([ + 'element' => $element, + 'filter' => $context->getParent()->row(), + ]); + } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 4aac0e98..49f0b83a 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -188,6 +188,7 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil return; } + $context->setId($element->id); $context->setName($element->type.'_'.$element->id); $context->setValue($element->value); $context->setDefaultValue($element->defaultValue); @@ -195,6 +196,8 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setFormBuilder($builder); $context->setTitle($element->title); $context->setLabel($element->label); + $context->setParent($element->getRelated('pid')); + $context->setSubmitOnChange($element->submitOnChange); if ($element->hideLabel) { $context->hideLabel(); diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 5dd4b63c..96f3d1af 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -78,6 +78,7 @@ services: # arguments: # - "@contao.framework" + HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice: ~ huh.filter.choice.field_options: class: HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice public: true From bb962334392ffc62884ec16e4739090c23a6adbb Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 16 Apr 2021 15:22:25 +0200 Subject: [PATCH 13/58] added more configuration options for choice type --- src/Config/FilterConfig.php | 2 ++ src/FilterType/FilterTypeContext.php | 48 ++++++++++++++++++++++++---- src/FilterType/Type/ChoiceType.php | 20 +++++++++++- src/Form/FilterType.php | 2 ++ 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 099f470a..ac988e67 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -659,6 +659,8 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setQueryBuilder($this->queryBuilder); $context->setParent($config->getRelated('pid')); $context->setSubmitOnChange($config->submitOnChange); + $context->setExpanded($config->expanded); + $context->setMultiple($config->multiple); $filter->buildQuery($context); } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 03800de7..8a6ca96b 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -15,9 +15,14 @@ class FilterTypeContext implements \IteratorAggregate { /** - * @var string + * @var string|array */ - private $defaultValue = ''; + private $defaultValue; + + /** + * @var bool + */ + private $expanded = false; /** * @var string */ @@ -48,6 +53,11 @@ class FilterTypeContext implements \IteratorAggregate */ private $isLabelHidden = false; + /** + * @var bool + */ + private $isMultiple = false; + /** * @var string */ @@ -82,9 +92,9 @@ class FilterTypeContext implements \IteratorAggregate private $title; /** - * @var string + * @var string|array */ - private $value = ''; + private $value; public function getName(): string { @@ -96,7 +106,10 @@ public function setName(string $name): void $this->name = $name; } - public function getValue(): string + /** + * @return array|string + */ + public function getValue() { if (empty($this->value)) { return $this->getDefaultValue(); @@ -105,7 +118,10 @@ public function getValue(): string return $this->value; } - public function setValue(string $value): void + /** + * @param string|array $value + */ + public function setValue($value): void { $this->value = $value; } @@ -261,4 +277,24 @@ public function setSubmitOnChange(bool $submitOnChange): void { $this->submitOnChange = $submitOnChange; } + + public function isMultiple(): bool + { + return $this->isMultiple; + } + + public function setMultiple(bool $isMultiple): void + { + $this->isMultiple = $isMultiple; + } + + public function isExpanded(): bool + { + return $this->expanded; + } + + public function setExpanded(bool $expanded): void + { + $this->expanded = $expanded; + } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index 0d18459e..e25ad284 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -54,7 +54,7 @@ public function buildForm($filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator,submitOnChange;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,submitOnChange,expanded,multiple;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; } public function getOperators(): array @@ -78,6 +78,8 @@ public function getOptions(FilterTypeContext $filterTypeContext): array { $options = parent::getOptions($filterTypeContext); $options['choices'] = array_flip($this->collectChoices($filterTypeContext)); + $options['choice_translation_domain'] = false; + $options['expanded'] = $filterTypeContext->isExpanded(); if ($filterTypeContext->isSubmitOnChange()) { if ($filterTypeContext->getParent()->asyncFormSubmit) { @@ -93,6 +95,22 @@ public function getOptions(FilterTypeContext $filterTypeContext): array } } + if (isset($options['attr']['placeholder'])) { + $options['attr']['data-placeholder'] = $options['attr']['placeholder']; + $options['placeholder'] = $options['attr']['placeholder']; + unset($options['attr']['placeholder']); + + $options['required'] = false; + $options['empty_data'] = true === $filterTypeContext->isMultiple() ? [] : ''; + } + + $options['multiple'] = $filterTypeContext->isMultiple(); + + // forgiving array handling + if (true === $filterTypeContext->isMultiple() && isset($options['data'])) { + $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; + } + return $options; } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 49f0b83a..0b7bea47 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -198,6 +198,8 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setLabel($element->label); $context->setParent($element->getRelated('pid')); $context->setSubmitOnChange($element->submitOnChange); + $context->setExpanded($element->expanded); + $context->setMultiple($element->multiple); if ($element->hideLabel) { $context->hideLabel(); From c8a1360612fca69bc94ed83ef22f42818dbf0ca0 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 16 Apr 2021 17:41:47 +0200 Subject: [PATCH 14/58] working on datetimetype --- src/Config/FilterConfig.php | 3 + .../FilterConfigElementContainer.php | 16 ++++ src/FilterType/FilterTypeContext.php | 45 ++++++++++ src/FilterType/Type/DateTimeType.php | 87 +++++++++++++++++-- src/Form/FilterType.php | 3 + .../contao/dca/tl_filter_config_element.php | 6 +- 6 files changed, 148 insertions(+), 12 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index ac988e67..5b5f32b0 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -661,6 +661,9 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setSubmitOnChange($config->submitOnChange); $context->setExpanded($config->expanded); $context->setMultiple($config->multiple); + $context->setDateTimeFormat($config->dateTimeFormat); + $context->setMinDateTime($config->minDateTime); + $context->setMaxDateTime($config->maxDateTime); $filter->buildQuery($context); } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index cf985010..8d213e35 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -88,4 +88,20 @@ public function getOperators(DataContainer $dc) return $this->typeCollection->getType($dc->activeRecord->type)->getOperators(); } + + public function getDateWidgetOptions(DataContainer $dc): array + { + if ($this->bundleConfig['filter']['disable_legacy_filers']) { + return [ + \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_CHOICE, + \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_TEXT, + \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, + ]; + } + + return [ + \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_CHOICE, + \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, + ]; + } } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 8a6ca96b..9eebb400 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -14,6 +14,11 @@ class FilterTypeContext implements \IteratorAggregate { + /** + * @var string + */ + private $dateTimeFormat; + /** * @var string|array */ @@ -58,6 +63,16 @@ class FilterTypeContext implements \IteratorAggregate */ private $isMultiple = false; + /** + * @var string + */ + private $maxDateTime; + + /** + * @var string + */ + private $minDateTime; + /** * @var string */ @@ -297,4 +312,34 @@ public function setExpanded(bool $expanded): void { $this->expanded = $expanded; } + + public function getDateTimeFormat(): string + { + return $this->dateTimeFormat; + } + + public function setDateTimeFormat(string $dateTimeFormat): void + { + $this->dateTimeFormat = $dateTimeFormat; + } + + public function getMaxDateTime(): string + { + return $this->maxDateTime; + } + + public function setMaxDateTime(string $maxDateTime): void + { + $this->maxDateTime = $maxDateTime; + } + + public function getMinDateTime(): string + { + return $this->minDateTime; + } + + public function setMinDateTime(string $minDateTime): void + { + $this->minDateTime = $minDateTime; + } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index df492150..4d3d3813 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -8,16 +8,29 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; +use Contao\Date; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use HeimrichHannot\UtilsBundle\Date\DateUtil; +use Symfony\Component\Form\Extension\Core\Type\DateTimeType as SymfonyDateTimeType; +use Symfony\Contracts\Translation\TranslatorInterface; class DateTimeType extends AbstractFilterType { const TYPE = 'date_time_type'; + protected DateUtil $dateUtil; - public static function test(): string - { - return 'test'; + public function __construct( + FilterQueryPartProcessor $filterQueryPartProcessor, + FilterQueryPartCollection $filterQueryPartCollection, + TranslatorInterface $translator, + DateUtil $dateUtil + ) { + parent::__construct($filterQueryPartProcessor, $filterQueryPartCollection, $translator); + $this->dateUtil = $dateUtil; } public static function getType(): string @@ -25,18 +38,78 @@ public static function getType(): string return static::TYPE; } - public function buildQuery(FilterTypeContext $filterTypeContext): string + public function buildQuery(FilterTypeContext $filterTypeContext) { - // TODO: Implement buildQuery() method. + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } public function buildForm($filterTypeContext) { - // TODO: Implement buildForm() method. + $builder = $filterTypeContext->getFormBuilder(); + + $builder->add($filterTypeContext->getName(), SymfonyDateTimeType::class, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string { - return parent::getPalette($prependPalette, $appendPalette); + return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;{visualization_legend},dateWidget,customLabel,hideLabel,addPlaceholder;'.$appendPalette; + } + + public function getOperators(): array + { + //remove this operators from the DatabaseUtil::OPERATORS array + $remove = [ + DatabaseUtil::OPERATOR_IN, + DatabaseUtil::OPERATOR_NOT_IN, + DatabaseUtil::OPERATOR_LIKE, + DatabaseUtil::OPERATOR_UNLIKE, + DatabaseUtil::OPERATOR_REGEXP, + DatabaseUtil::OPERATOR_NOT_REGEXP, + DatabaseUtil::OPERATOR_IS_NULL, + DatabaseUtil::OPERATOR_IS_NOT_NULL, + DatabaseUtil::OPERATOR_IS_EMPTY, + DatabaseUtil::OPERATOR_IS_NOT_EMPTY, + ]; + + return array_values(array_diff(parent::getOperators(), $remove)); + } + + public function getOptions(FilterTypeContext $filterTypeContext): array + { + $options = parent::getOptions($filterTypeContext); + +// $time = time(); + + $format = $filterTypeContext->getDateTimeFormat() ?: 'd.m.Y H:i'; + $options['html5'] = true; + $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + + if (true === $options['html5']) { + if ($filterTypeContext->getMinDateTime()) { + $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + } + + if ($filterTypeContext->getMaxDateTime()) { + $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + } + } + + $options['group_attr']['class'] = isset($options['group_attr']['class']) ? $options['group_attr']['class'].' datepicker timepicker' : 'datepicker timepicker'; + $options['attr']['data-iso8601-format'] = $this->dateUtil->transformPhpDateFormatToISO8601($format); + $options['attr']['data-enable-time'] = 'true'; + $options['attr']['data-date-format'] = $format; + + if ($filterTypeContext->getMinDateTime()) { + $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); + } + + if ($filterTypeContext->getMaxDateTime()) { + $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); + } + +// $options['widget'] = 'choice'; + $options['widget'] = 'single_text'; + + return $options; } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 0b7bea47..7cd172c6 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -200,6 +200,9 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setSubmitOnChange($element->submitOnChange); $context->setExpanded($element->expanded); $context->setMultiple($element->multiple); + $context->setDateTimeFormat($element->dateTimeFormat); + $context->setMinDateTime($element->minDateTime); + $context->setMaxDateTime($element->maxDateTime); if ($element->hideLabel) { $context->hideLabel(); diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 9e353820..f5edbcb6 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -528,11 +528,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, - 'options' => [ - \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_CHOICE, - \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_TEXT, - \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, - ], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getDateWidgetOptions'], 'eval' => ['tl_class' => 'w50', 'chosen' => true, 'submitOnChange' => true], 'sql' => "varchar(16) NOT NULL default ''", ], From 900ad15bfc5fd9423bd21ed9e31fa3eae96dfcc2 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 19 Apr 2021 14:33:48 +0200 Subject: [PATCH 15/58] added whereQueryBuilder merhod to QueryPart class, working on dateTimeType --- src/Config/FilterConfig.php | 15 +- src/Filter/FilterQueryPart.php | 278 ++++++++++++++++++++++-- src/Filter/FilterQueryPartProcessor.php | 13 +- src/FilterType/FilterTypeContext.php | 6 +- 4 files changed, 281 insertions(+), 31 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 5b5f32b0..52fd270c 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -15,6 +15,7 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; +use HeimrichHannot\FilterBundle\Filter\FilterQueryPart; use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; @@ -100,7 +101,11 @@ class FilterConfig implements \JsonSerializable * @var FilterQueryPartCollection */ protected $filterQueryPartCollection; - protected EventDispatcherInterface $eventDispatcher; + + /** + * @var EventDispatcherInterface + */ + protected $eventDispatcher; /** * @var ContainerInterface @@ -284,8 +289,16 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip //apply parts from FilterQueryPartCollection $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection)); + /** + * @var FilterQueryPart + */ foreach ($event->getPartsCollection()->getParts() as $part) { $this->queryBuilder->andWhere($part->query); + $this->queryBuilder->setParameter( + $part->getWildcard(), + $part->getValue(), + $part->getValueType() + ); } } diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php index 8ef828d8..9766b541 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/Filter/FilterQueryPart.php @@ -8,33 +8,50 @@ namespace HeimrichHannot\FilterBundle\Filter; +use Contao\Controller; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\Query\Parameter; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; -class FilterQueryPart +class FilterQueryPart extends EntityManager { /** * @var string */ - public string $name; - + public $name; + /** + * @var Parameter + */ + public $parameter; /** * @var string */ public $query; + /** - * @var DatabaseUtil + * @var string */ - protected $databaseUtil; + public $wildcard; + /** + * @var string|int|array|\DateTime + */ + public $value; + + /** + * @var string + */ + public $valueType; /** * @var int */ protected $filterElementId; - public function __construct(FilterTypeContext $context, DatabaseUtil $databaseUtil) + public function __construct(FilterTypeContext $context) { - $this->databaseUtil = $databaseUtil; $this->name = $context->getName(); $this->filterElementId = $context->getId(); $this->query = $this->composeQuery($context); @@ -50,15 +67,246 @@ public function setFilterElementId(int $filterElementId): void $this->filterElementId = $filterElementId; } - private function composeQuery(FilterTypeContext $context): string + /** + * @param null $value + * @param array $options + * { + * wildcardSuffix: string, + * valueType: string + * } + */ + public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $options = []): string { - return $this->databaseUtil->composeWhereForQueryBuilder( - $context->getQueryBuilder(), - $context->getField(), - $context->getOperator(), - null, - $context->getValue(), - ['wildcardSuffix' => $context->getId()] + $queryBuilder = $this->createQueryBuilder(); + + $dca = $GLOBALS['TL_DCA']['tl_filter_config_element']['']; + + $valueType = $options['valueType'] ?? null; + $wildcardSuffix = $options['wildcardSuffix'] ?? ''; + $wildcard = ':'.str_replace('.', '_', $field).$wildcardSuffix; + $where = ''; + + if (\is_string($value)) { + $value = Controller::replaceInsertTags(\is_array($value) ? implode(' ', $value) : $value, false); + } + + switch ($operator) { + case DatabaseUtil::OPERATOR_LIKE: + $where = $queryBuilder->expr()->like($field, $wildcard); + $this->applyParameterValues($wildcard, '%'.$value.'%', $valueType); + + break; + + case DatabaseUtil::OPERATOR_UNLIKE: + $where = $queryBuilder->expr()->notLike($field, $wildcard); + $this->applyParameterValues($wildcard, '%'.$value.'%', $valueType); + + break; + + case DatabaseUtil::OPERATOR_EQUAL: + $where = $queryBuilder->expr()->eq($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_UNEQUAL: + $where = $queryBuilder->expr()->neq($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_LOWER: + $where = $queryBuilder->expr()->lt($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_LOWER_EQUAL: + $where = $queryBuilder->expr()->lte($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_GREATER: + $where = $queryBuilder->expr()->gt($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_GREATER_EQUAL: + $where = $queryBuilder->expr()->gte($field, $wildcard); + $this->applyParameterValues($wildcard, $value, $valueType); + + break; + + case DatabaseUtil::OPERATOR_IN: + $value = array_filter(!\is_array($value) ? explode(',', $value) : $value); + + // if empty add an unfulfillable condition in order to avoid an sql error + if (empty($value)) { + $where = $queryBuilder->expr()->eq(1, 2); + } else { + $where = $queryBuilder->expr()->in($field, $wildcard); + $preparedValue = array_map( + function ($val) { + return addslashes(Controller::replaceInsertTags(trim($val), false)); + }, + $value + ); + $this->applyParameterValues($wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + } + + break; + + case DatabaseUtil::OPERATOR_NOT_IN: + $value = array_filter(!\is_array($value) ? explode(',', $value) : $value); + + // if empty add an unfulfillable condition in order to avoid an sql error + if (empty($value)) { + $where = $queryBuilder->expr()->eq(1, 2); + } else { + $where = $queryBuilder->expr()->notIn($field, $wildcard); + $preparedValue = array_map( + function ($val) { + return addslashes(Controller::replaceInsertTags(trim($val), false)); + }, + $value + ); + $this->applyParameterValues($wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + } + + break; + + case DatabaseUtil::OPERATOR_IS_NULL: + $where = $queryBuilder->expr()->isNull($field); + + break; + + case DatabaseUtil::OPERATOR_IS_NOT_NULL: + $where = $queryBuilder->expr()->isNotNull($field); + + break; + + case DatabaseUtil::OPERATOR_IS_EMPTY: + $where = $queryBuilder->expr()->eq($field, '\'\''); + + break; + + case DatabaseUtil::OPERATOR_IS_NOT_EMPTY: + $where = $queryBuilder->expr()->neq($field, '\'\''); + + break; + + case DatabaseUtil::OPERATOR_REGEXP: + case DatabaseUtil::OPERATOR_NOT_REGEXP: + $where = $field.(DatabaseUtil::OPERATOR_NOT_REGEXP == $operator ? ' NOT REGEXP ' : ' REGEXP ').$wildcard; + + if (\is_array($dca) && isset($dca['eval']['multiple']) && $dca['eval']['multiple']) { + // match a serialized blob + if (\is_array($value)) { + // build a regexp alternative, e.g. (:"1";|:"2";) + $this->applyParameterValues( + $wildcard, + '('.implode( + '|', + array_map( + function ($val) { + return ':"'.Controller::replaceInsertTags($val, false).'";'; + }, + $value + ) + ).')', + $valueType + ); + } else { + $this->applyParameterValues($wildcard, ':"'.$value.'";', $valueType); + } + } else { + // TODO: this makes no sense, yet + $this->applyParameterValues($wildcard, $value, $valueType); + } + + break; + } + + return $where; + } + + public function getParameter(): Parameter + { + return $this->parameter; + } + + public function setParameter(Parameter $parameter): void + { + $this->parameter = $parameter; + } + + public function applyParameterValues(string $wildcard, $value, string $valueType): void + { + $this->setWildcard($wildcard); + $this->setValue($value); + $this->setValueType($valueType); + } + + public function getWildcard(): string + { + return $this->wildcard; + } + + public function setWildcard(string $wildcard): void + { + $this->wildcard = $wildcard; + } + + /** + * @return array|\DateTime|int|string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param array|\DateTime|int|string $value + */ + public function setValue($value): void + { + $this->value = $value; + } + + public function getValueType(): string + { + return $this->valueType; + } + + public function setValueType(string $valueType): void + { + $this->valueType = $valueType; + } + + private function composeQuery(FilterTypeContext $filterTypeContext): string + { + $options = [ + 'wildcardSuffix' => $filterTypeContext->getId(), + 'valueType' => null, + ]; + + if ($filterTypeContext->getValue() instanceof \DateTime) { + /** + * @var \DateTime + */ + $date = $filterTypeContext->getValue(); + + $filterTypeContext->setValue($date->getTimeStamp()); + $options['valueType'] = Types::INTEGER; + } + + return $this->composeWhereForQueryBuilder( + $filterTypeContext->getField(), + $filterTypeContext->getOperator(), + $filterTypeContext->getValue(), + $options ); } } diff --git a/src/Filter/FilterQueryPartProcessor.php b/src/Filter/FilterQueryPartProcessor.php index 2db585a0..4a60a012 100644 --- a/src/Filter/FilterQueryPartProcessor.php +++ b/src/Filter/FilterQueryPartProcessor.php @@ -9,22 +9,11 @@ namespace HeimrichHannot\FilterBundle\Filter; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; -use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; class FilterQueryPartProcessor { - /** - * @var DatabaseUtil - */ - protected $databaseUtil; - - public function __construct(DatabaseUtil $databaseUtil) - { - $this->databaseUtil = $databaseUtil; - } - public function composeQueryPart(FilterTypeContext $context): FilterQueryPart { - return new FilterQueryPart($context, $this->databaseUtil); + return new FilterQueryPart($context); } } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 9eebb400..0fd5e1c4 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -107,7 +107,7 @@ class FilterTypeContext implements \IteratorAggregate private $title; /** - * @var string|array + * @var string|array|int */ private $value; @@ -122,7 +122,7 @@ public function setName(string $name): void } /** - * @return array|string + * @return array|string|int */ public function getValue() { @@ -134,7 +134,7 @@ public function getValue() } /** - * @param string|array $value + * @param string|array|int $value */ public function setValue($value): void { From 79c4658b5d35f99508ce59f80acec343edca90d5 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 19 Apr 2021 15:10:56 +0200 Subject: [PATCH 16/58] removed queryBuilder from FilterTypeContext, added dca to FilterQueryPart->composerWhereForQueryBuilder --- src/Config/FilterConfig.php | 3 +-- src/Filter/FilterQueryPart.php | 34 +++++++++---------------- src/Filter/FilterQueryPartProcessor.php | 10 +++++++- src/FilterType/FilterTypeContext.php | 16 ------------ src/FilterType/Type/DateTimeType.php | 6 ++++- src/Resources/config/services.yml | 1 - 6 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 52fd270c..e67d47ff 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -289,7 +289,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip //apply parts from FilterQueryPartCollection $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection)); - /** + /* * @var FilterQueryPart */ foreach ($event->getPartsCollection()->getParts() as $part) { @@ -669,7 +669,6 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setOperator($config->operator); $context->setValue($this->getData()[$context->getName()] ?: ''); $context->setDefaultValue($config->defaultValue); - $context->setQueryBuilder($this->queryBuilder); $context->setParent($config->getRelated('pid')); $context->setSubmitOnChange($config->submitOnChange); $context->setExpanded($config->expanded); diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php index 9766b541..5047215e 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/Filter/FilterQueryPart.php @@ -10,22 +10,17 @@ use Contao\Controller; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Query\Parameter; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; -class FilterQueryPart extends EntityManager +class FilterQueryPart { /** * @var string */ public $name; - /** - * @var Parameter - */ - public $parameter; /** * @var string */ @@ -50,8 +45,14 @@ class FilterQueryPart extends EntityManager */ protected $filterElementId; - public function __construct(FilterTypeContext $context) + /** + * @var Connection + */ + protected $connection; + + public function __construct(FilterTypeContext $context, Connection $connection) { + $this->connection = $connection; $this->name = $context->getName(); $this->filterElementId = $context->getId(); $this->query = $this->composeQuery($context); @@ -75,11 +76,9 @@ public function setFilterElementId(int $filterElementId): void * valueType: string * } */ - public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $options = []): string + public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $dca = null, array $options = []): string { - $queryBuilder = $this->createQueryBuilder(); - - $dca = $GLOBALS['TL_DCA']['tl_filter_config_element']['']; + $queryBuilder = new QueryBuilder($this->connection); $valueType = $options['valueType'] ?? null; $wildcardSuffix = $options['wildcardSuffix'] ?? ''; @@ -232,16 +231,6 @@ function ($val) { return $where; } - public function getParameter(): Parameter - { - return $this->parameter; - } - - public function setParameter(Parameter $parameter): void - { - $this->parameter = $parameter; - } - public function applyParameterValues(string $wildcard, $value, string $valueType): void { $this->setWildcard($wildcard); @@ -306,6 +295,7 @@ private function composeQuery(FilterTypeContext $filterTypeContext): string $filterTypeContext->getField(), $filterTypeContext->getOperator(), $filterTypeContext->getValue(), + $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']][$filterTypeContext->getField()], $options ); } diff --git a/src/Filter/FilterQueryPartProcessor.php b/src/Filter/FilterQueryPartProcessor.php index 4a60a012..fb7677be 100644 --- a/src/Filter/FilterQueryPartProcessor.php +++ b/src/Filter/FilterQueryPartProcessor.php @@ -8,12 +8,20 @@ namespace HeimrichHannot\FilterBundle\Filter; +use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; class FilterQueryPartProcessor { + protected Connection $connection; + + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + public function composeQueryPart(FilterTypeContext $context): FilterQueryPart { - return new FilterQueryPart($context); + return new FilterQueryPart($context, $this->connection); } } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 0fd5e1c4..aa72158c 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -9,7 +9,6 @@ namespace HeimrichHannot\FilterBundle\FilterType; use Contao\Model; -use Doctrine\DBAL\Query\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class FilterTypeContext implements \IteratorAggregate @@ -91,11 +90,6 @@ class FilterTypeContext implements \IteratorAggregate */ private $placeholder = null; - /** - * @var QueryBuilder - */ - private $queryBuilder; - /** * @var bool */ @@ -273,16 +267,6 @@ public function hideLabel(): void $this->isLabelHidden = true; } - public function getQueryBuilder(): QueryBuilder - { - return $this->queryBuilder; - } - - public function setQueryBuilder(QueryBuilder $queryBuilder): void - { - $this->queryBuilder = $queryBuilder; - } - public function isSubmitOnChange(): bool { return $this->submitOnChange; diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 4d3d3813..3fd44c92 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -21,7 +21,11 @@ class DateTimeType extends AbstractFilterType { const TYPE = 'date_time_type'; - protected DateUtil $dateUtil; + + /** + * @var DateUtil + */ + protected $dateUtil; public function __construct( FilterQueryPartProcessor $filterQueryPartProcessor, diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 96f3d1af..0ea5d741 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -188,7 +188,6 @@ services: huh.filter.filter_type.collection: '@HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection' HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection: - huh.filter.filter_query_part_collection: '@HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection' HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor: From 7dd779f0d0bad52aaa8cb560747421b0651f082c Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 19 Apr 2021 17:45:13 +0200 Subject: [PATCH 17/58] first buttonType implementation --- .../FilterConfigElementContainer.php | 5 +++ .../LoadDataContainerListener.php | 2 +- src/Filter/FilterQueryPart.php | 10 ++--- src/FilterType/AbstractFilterType.php | 6 ++- src/FilterType/FilterTypeContext.php | 29 ++++++++++++++ src/FilterType/Type/ButtonType.php | 40 +++++++++++++++++-- src/Form/FilterType.php | 2 + src/Model/FilterConfigElementModel.php | 1 + .../contao/dca/tl_filter_config_element.php | 13 ++++++ .../languages/de/tl_filter_config_element.php | 4 ++ 10 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 8d213e35..1f1cbe62 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -104,4 +104,9 @@ public function getDateWidgetOptions(DataContainer $dc): array \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, ]; } + + public function getButtonTypes(DataContainer $dc): array + { + return ['button', 'reset', 'submit']; + } } diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index 39447804..e76e75c1 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -57,7 +57,7 @@ private function prepareFilterConfigElementDca() $prependPalette = '{initial_legend},isInitial;'.$prependPalette; } - $appendPalette = '{publish_legend},published;'; + $appendPalette = '{expert_legend},cssClass;{publish_legend},published;'; $dca['palettes'][$key] = $type->getPalette($prependPalette, $appendPalette); } diff --git a/src/Filter/FilterQueryPart.php b/src/Filter/FilterQueryPart.php index 5047215e..49c70b3a 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/Filter/FilterQueryPart.php @@ -37,7 +37,7 @@ class FilterQueryPart public $value; /** - * @var string + * @var int|string|null */ public $valueType; /** @@ -231,7 +231,7 @@ function ($val) { return $where; } - public function applyParameterValues(string $wildcard, $value, string $valueType): void + public function applyParameterValues(string $wildcard, $value, $valueType): void { $this->setWildcard($wildcard); $this->setValue($value); @@ -264,12 +264,12 @@ public function setValue($value): void $this->value = $value; } - public function getValueType(): string + public function getValueType() { return $this->valueType; } - public function setValueType(string $valueType): void + public function setValueType($valueType): void { $this->valueType = $valueType; } @@ -295,7 +295,7 @@ private function composeQuery(FilterTypeContext $filterTypeContext): string $filterTypeContext->getField(), $filterTypeContext->getOperator(), $filterTypeContext->getValue(), - $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']][$filterTypeContext->getField()], + $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], $options ); } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index b8024816..d2413975 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -96,10 +96,14 @@ public function getOptions(FilterTypeContext $context): array { $options = []; - if ('' !== $context->getPlaceholder()) { + if ($context->getPlaceholder()) { $options['attr']['placeholder'] = $this->translator->trans($context->getPlaceholder(), ['%label%' => $this->translator->trans($options['label']) ?: $context->getTitle()]); } + if ($context->getCssClass()) { + $options['attr']['class'] = $context->getCssClass(); + } + $options['label'] = $context->getLabel() ?: $context->getTitle(); // sr-only style for non-bootstrap projects is shipped within the filter_form_* templates diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index aa72158c..fc33305a 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -13,6 +13,15 @@ class FilterTypeContext implements \IteratorAggregate { + /** + * @var string + */ + private $buttonType; + + /** + * @var string + */ + private $cssClass; /** * @var string */ @@ -326,4 +335,24 @@ public function setMinDateTime(string $minDateTime): void { $this->minDateTime = $minDateTime; } + + public function getCssClass(): string + { + return $this->cssClass; + } + + public function setCssClass(string $class): void + { + $this->cssClass = $class; + } + + public function getButtonType(): string + { + return $this->buttonType; + } + + public function setButtonType(string $buttonType): void + { + $this->buttonType = $buttonType; + } } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index d3bb1543..3fbb99c0 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -10,10 +10,23 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use Symfony\Component\Form\Extension\Core\Type\ButtonType as SymfonyButtonType; +use Symfony\Component\Form\Extension\Core\Type\ResetType as SymfonyResetType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType as SymfonySubmitType; class ButtonType extends AbstractFilterType { const TYPE = 'button_type'; + const GROUP = 'button'; + + const BUTTON_TYPE_BUTTON = 'button'; + const BUTTON_TYPE_RESET = 'reset'; + const BUTTON_TYPE_SUBMIT = 'submit'; + const BUTTON_TYPES = [ + self::BUTTON_TYPE_BUTTON, + self::BUTTON_TYPE_RESET, + self::BUTTON_TYPE_SUBMIT, + ]; public static function getType(): string { @@ -22,16 +35,35 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext): string { - // TODO: Implement buildQuery() method. + return ''; } - public function buildForm($filterTypeContext) + public function buildForm(FilterTypeContext $filterTypeContext) { - // TODO: Implement buildForm() method. + $builder = $filterTypeContext->getFormBuilder(); + + switch ($filterTypeContext->getButtonType()) { + case static::BUTTON_TYPE_RESET: + $symfonyButton = SymfonyResetType::class; + + break; + + case static::BUTTON_TYPE_SUBMIT: + $symfonyButton = SymfonySubmitType::class; + + break; + + default: + $symfonyButton = SymfonyButtonType::class; + + break; + } + + $builder->add($filterTypeContext->getName(), $symfonyButton, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string { - return parent::getPalette($prependPalette, $appendPalette); + return $prependPalette.'{config_legend},buttonType;{visualization_legend},customLabel;'.$appendPalette; } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 7cd172c6..9b017090 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -203,6 +203,8 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setDateTimeFormat($element->dateTimeFormat); $context->setMinDateTime($element->minDateTime); $context->setMaxDateTime($element->maxDateTime); + $context->setCssClass($element->cssClass); + $context->setButtonType($element->buttonType); if ($element->hideLabel) { $context->hideLabel(); diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index 2d1c6736..616488f7 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -101,6 +101,7 @@ * @property string $start * @property string $stop * @property bool $useRangeSlider + * @property string $buttonType * * @method FilterConfigElementModel|null findById($id, array $opt = []) * @method FilterConfigElementModel|null findByPk($id, array $opt = []) diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index f5edbcb6..521ef2cb 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -886,6 +886,19 @@ 'eval' => ['chosen' => true, 'includeBlankOption' => true, 'tl_class' => 'w50', 'mandatory' => true], 'sql' => "varchar(64) NOT NULL default ''", ], + 'buttonType' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['buttonType'], + 'exclude' => true, + 'search' => true, + 'inputType' => 'select', + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getButtonTypes'], + 'eval' => [ + 'tl_class' => 'w50', + 'mandatory' => true, + 'includeBlankOption' => true, + ], + 'sql' => "varchar(16) NOT NULL default ''", + ], 'cssClass' => [ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['cssClass'], 'exclude' => true, diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index 51a51fa9..7031da50 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -195,6 +195,10 @@ \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE => 'aktuelles Mitglied', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE_ID => 'ID', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE_USERNAME => 'Benutzername', + \HeimrichHannot\FilterBundle\FilterType\Type\TextType::TYPE => 'Text', + \HeimrichHannot\FilterBundle\FilterType\Type\ChoiceType::TYPE => 'Choice', + \HeimrichHannot\FilterBundle\FilterType\Type\DateTimeType::TYPE => 'Datum & Zeit', + \HeimrichHannot\FilterBundle\FilterType\Type\ButtonType::TYPE => 'Button', ], 'roundingMode' => [ \Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer::ROUND_DOWN => 'Abrunden (zu 0 hin)', From 2efd6bada8e165c7c422ecdd0f0055272b42c21f Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 20 Apr 2021 12:00:29 +0200 Subject: [PATCH 18/58] added html5 to datetimeType, working button type --- src/Config/FilterConfig.php | 2 +- .../FilterConfigElementContainer.php | 3 +- src/FilterType/AbstractFilterType.php | 24 +++++---- src/FilterType/FilterTypeContext.php | 49 +++++++++++++++++++ src/FilterType/Type/ButtonType.php | 12 +++++ src/FilterType/Type/DateTimeType.php | 15 +++--- src/Form/FilterType.php | 6 ++- 7 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index e67d47ff..880fb198 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -15,7 +15,6 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPart; use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; @@ -676,6 +675,7 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setDateTimeFormat($config->dateTimeFormat); $context->setMinDateTime($config->minDateTime); $context->setMaxDateTime($config->maxDateTime); + $context->setCustomLabel($config->customLabel); $filter->buildQuery($context); } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 1f1cbe62..bbdc0aba 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -12,6 +12,7 @@ use HeimrichHannot\FilterBundle\Choice\TypeChoice; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; @@ -107,6 +108,6 @@ public function getDateWidgetOptions(DataContainer $dc): array public function getButtonTypes(DataContainer $dc): array { - return ['button', 'reset', 'submit']; + return ButtonType::BUTTON_TYPES; } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index d2413975..ecd4f7fc 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -92,28 +92,26 @@ public function buildQuery(FilterTypeContext $filterTypeContext) $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } - public function getOptions(FilterTypeContext $context): array + public function getOptions(FilterTypeContext $filterTypeContext): array { $options = []; + $options['label'] = $filterTypeContext->isCustomLabel() ? $filterTypeContext->getLabel() : $filterTypeContext->getTitle(); - if ($context->getPlaceholder()) { - $options['attr']['placeholder'] = $this->translator->trans($context->getPlaceholder(), ['%label%' => $this->translator->trans($options['label']) ?: $context->getTitle()]); + // sr-only style for non-bootstrap projects is shipped within the filter_form_* templates + if (true === $filterTypeContext->isLabelHidden()) { + $options['label_attr'] = ['class' => 'sr-only']; } + // always label for screen readers + $options['attr']['aria-label'] = $this->translator->trans($filterTypeContext->isCustomLabel() ? $filterTypeContext->getLabel() : $filterTypeContext->getTitle()); - if ($context->getCssClass()) { - $options['attr']['class'] = $context->getCssClass(); + if ($filterTypeContext->getPlaceholder()) { + $options['attr']['placeholder'] = $this->translator->trans($filterTypeContext->getPlaceholder(), ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); } - $options['label'] = $context->getLabel() ?: $context->getTitle(); - - // sr-only style for non-bootstrap projects is shipped within the filter_form_* templates - if (true === $context->isLabelHidden()) { - $options['label_attr'] = ['class' => 'sr-only']; + if ($filterTypeContext->getCssClass()) { + $options['attr']['class'] = $filterTypeContext->getCssClass(); } - // always label for screen readers - $options['attr']['aria-label'] = $this->translator->trans($context->getLabel() ?: $context->getTitle()); - return $options; } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index fc33305a..650c3e90 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -22,6 +22,12 @@ class FilterTypeContext implements \IteratorAggregate * @var string */ private $cssClass; + + /** + * @var bool + */ + private $customLabel = false; + /** * @var string */ @@ -36,6 +42,7 @@ class FilterTypeContext implements \IteratorAggregate * @var bool */ private $expanded = false; + /** * @var string */ @@ -46,6 +53,11 @@ class FilterTypeContext implements \IteratorAggregate */ private $formBuilder; + /** + * @var bool + */ + private $html5 = false; + /** * @var int */ @@ -85,10 +97,12 @@ class FilterTypeContext implements \IteratorAggregate * @var string */ private $name = ''; + /** * @var string */ private $operator = ''; + /** * @var Model */ @@ -114,6 +128,11 @@ class FilterTypeContext implements \IteratorAggregate */ private $value; + /** + * @var string + */ + private $widget; + public function getName(): string { return $this->name; @@ -355,4 +374,34 @@ public function setButtonType(string $buttonType): void { $this->buttonType = $buttonType; } + + public function isCustomLabel(): bool + { + return $this->customLabel; + } + + public function setCustomLabel(bool $customLabel): void + { + $this->customLabel = $customLabel; + } + + public function isHtml5(): bool + { + return $this->html5; + } + + public function setHtml5(bool $html5): void + { + $this->html5 = $html5; + } + + public function getWidget(): string + { + return $this->widget; + } + + public function setWidget(string $widget): void + { + $this->widget = $widget; + } } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index 3fbb99c0..5cc159e7 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -22,6 +22,7 @@ class ButtonType extends AbstractFilterType const BUTTON_TYPE_BUTTON = 'button'; const BUTTON_TYPE_RESET = 'reset'; const BUTTON_TYPE_SUBMIT = 'submit'; + const BUTTON_TYPES = [ self::BUTTON_TYPE_BUTTON, self::BUTTON_TYPE_RESET, @@ -66,4 +67,15 @@ public function getPalette(string $prependPalette, string $appendPalette): strin { return $prependPalette.'{config_legend},buttonType;{visualization_legend},customLabel;'.$appendPalette; } + + public function getOptions(FilterTypeContext $filterTypeContext): array + { + $options = parent::getOptions($filterTypeContext); + +// if ($filterTypeContext->getButtonType() === static::BUTTON_TYPE_RESET) { +// $options['attr']['onclick'] = 'this.form.submit()'; +// } + + return $options; + } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 3fd44c92..f7bae160 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -56,7 +56,7 @@ public function buildForm($filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;{visualization_legend},dateWidget,customLabel,hideLabel,addPlaceholder;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;{visualization_legend},html5,dateWidget,customLabel,hideLabel,addPlaceholder;'.$appendPalette; } public function getOperators(): array @@ -81,14 +81,12 @@ public function getOperators(): array public function getOptions(FilterTypeContext $filterTypeContext): array { $options = parent::getOptions($filterTypeContext); - -// $time = time(); - $format = $filterTypeContext->getDateTimeFormat() ?: 'd.m.Y H:i'; - $options['html5'] = true; - $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + $options['html5'] = $filterTypeContext->isHtml5(); if (true === $options['html5']) { + $options['date_format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + if ($filterTypeContext->getMinDateTime()) { $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used } @@ -96,6 +94,8 @@ public function getOptions(FilterTypeContext $filterTypeContext): array if ($filterTypeContext->getMaxDateTime()) { $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used } + } else { + $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); } $options['group_attr']['class'] = isset($options['group_attr']['class']) ? $options['group_attr']['class'].' datepicker timepicker' : 'datepicker timepicker'; @@ -111,8 +111,7 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); } -// $options['widget'] = 'choice'; - $options['widget'] = 'single_text'; + $options['widget'] = $filterTypeContext->getWidget(); return $options; } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 9b017090..f250d803 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -182,12 +182,11 @@ protected function buildElements(FormBuilderInterface $builder, array $options) protected function buildFilterTypeElement(FilterConfigElementModel $element, FilterTypeInterface $filterType, FormBuilderInterface $builder) { - $context = new FilterTypeContext(); - if ($element->isInitial) { return; } + $context = new FilterTypeContext(); $context->setId($element->id); $context->setName($element->type.'_'.$element->id); $context->setValue($element->value); @@ -205,6 +204,9 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setMaxDateTime($element->maxDateTime); $context->setCssClass($element->cssClass); $context->setButtonType($element->buttonType); + $context->setCustomLabel($element->customLabel); + $context->setHtml5($element->html5); + $context->setWidget($element->dateWidget); if ($element->hideLabel) { $context->hideLabel(); From b6dd843e985e5b198d4f899eda014876c7bb9823 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 20 Apr 2021 16:35:31 +0200 Subject: [PATCH 19/58] fixed resetButton to be submitButton, and still reset the form --- src/Config/FilterConfig.php | 21 ++++++++++++-------- src/Filter/Filter.php | 27 -------------------------- src/FilterType/Type/ButtonType.php | 16 --------------- src/Form/FilterType.php | 2 +- src/Model/FilterConfigElementModel.php | 5 +++++ 5 files changed, 19 insertions(+), 52 deletions(-) delete mode 100644 src/Filter/Filter.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 880fb198..1757d2d8 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -19,6 +19,7 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; +use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; use HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension; use HeimrichHannot\FilterBundle\Form\FilterType; @@ -198,6 +199,12 @@ public function buildForm(array $data = []) $this->builder = $factory->createNamedBuilder($this->filter['name'], FilterType::class, $data, $options); + foreach ($this->elements as $element) { + if (ButtonType::TYPE === $element->type && ButtonType::BUTTON_TYPE_RESET === $element->buttonType) { + $this->addResetName($element->getElementName()); + } + } + $this->mapFormsToData(); } @@ -251,17 +258,13 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip return; } - if ($types[$element->type] instanceof AbstractFilterType) { - $this->processFilterType($element, $types[$element->type]); - } - if (!isset($types[$element->type]) || \in_array($element->id, $skipElements) || $mode === static::QUERY_BUILDER_MODE_INITIAL_ONLY && !$element->isInitial || $mode === static::QUERY_BUILDER_MODE_SKIP_INITIAL && $element->isInitial) { continue; } - if (!\is_array($types[$element->type])) { + if (!\is_array($types[$element->type]) && $types[$element->type] instanceof AbstractFilterType) { $this->processFilterType($element, $types[$element->type]); continue; @@ -286,6 +289,8 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip } //apply parts from FilterQueryPartCollection + /** @noinspection PhpMethodParametersCountMismatchInspection */ + /** @noinspection PhpParamsInspection */ $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection)); /* @@ -659,11 +664,11 @@ public function jsonSerialize() return get_object_vars($this); } - protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filter) + protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filterType) { $context = new FilterTypeContext(); $context->setId($config->id); - $context->setName($config->type.'_'.$config->id); + $context->setName($config->getElementName()); $context->setField($config->field); $context->setOperator($config->operator); $context->setValue($this->getData()[$context->getName()] ?: ''); @@ -677,7 +682,7 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setMaxDateTime($config->maxDateTime); $context->setCustomLabel($config->customLabel); - $filter->buildQuery($context); + $filterType->buildQuery($context); } protected function isResetButtonClicked(FormInterface $form): bool diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php deleted file mode 100644 index ae352fac..00000000 --- a/src/Filter/Filter.php +++ /dev/null @@ -1,27 +0,0 @@ -getReflectionClass()->name) { - case 'HeimrichHannot/FilterBundle/FilterType/Type/TextType': - return sprintf('%s.name = %s', $targetTableAlias, $this->getParameter('name')); - - break; - } - - return ''; - } -} diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index 5cc159e7..9e20c3d9 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -11,7 +11,6 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use Symfony\Component\Form\Extension\Core\Type\ButtonType as SymfonyButtonType; -use Symfony\Component\Form\Extension\Core\Type\ResetType as SymfonyResetType; use Symfony\Component\Form\Extension\Core\Type\SubmitType as SymfonySubmitType; class ButtonType extends AbstractFilterType @@ -45,10 +44,6 @@ public function buildForm(FilterTypeContext $filterTypeContext) switch ($filterTypeContext->getButtonType()) { case static::BUTTON_TYPE_RESET: - $symfonyButton = SymfonyResetType::class; - - break; - case static::BUTTON_TYPE_SUBMIT: $symfonyButton = SymfonySubmitType::class; @@ -67,15 +62,4 @@ public function getPalette(string $prependPalette, string $appendPalette): strin { return $prependPalette.'{config_legend},buttonType;{visualization_legend},customLabel;'.$appendPalette; } - - public function getOptions(FilterTypeContext $filterTypeContext): array - { - $options = parent::getOptions($filterTypeContext); - -// if ($filterTypeContext->getButtonType() === static::BUTTON_TYPE_RESET) { -// $options['attr']['onclick'] = 'this.form.submit()'; -// } - - return $options; - } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index f250d803..79cc3b15 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -188,7 +188,7 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context = new FilterTypeContext(); $context->setId($element->id); - $context->setName($element->type.'_'.$element->id); + $context->setName($element->getElementName()); $context->setValue($element->value); $context->setDefaultValue($element->defaultValue); $context->setPlaceholder($element->placeholder); diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index 616488f7..030db2cb 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -271,4 +271,9 @@ public function jsonSerialize() { return get_object_vars($this); } + + public function getElementName(): string + { + return $this->type.'_'.$this->id; + } } From c477fa495ad3e431006019c1403b4f1ff3127a5b Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 20 Apr 2021 17:42:01 +0200 Subject: [PATCH 20/58] refactored queryPart and queryPartProcessor --- src/Config/FilterConfig.php | 8 +- src/Event/ModifyFilterQueryPartsEvent.php | 2 +- src/Filter/FilterQueryPartProcessor.php | 27 --- src/FilterQuery/FilterQueryPart.php | 84 ++++++++ .../FilterQueryPartCollection.php | 2 +- .../FilterQueryPartProcessor.php} | 179 ++++++------------ src/FilterType/AbstractFilterType.php | 4 +- src/FilterType/Type/ChoiceType.php | 4 +- src/FilterType/Type/DateTimeType.php | 4 +- src/Resources/config/services.yml | 8 +- 10 files changed, 156 insertions(+), 166 deletions(-) delete mode 100644 src/Filter/FilterQueryPartProcessor.php create mode 100644 src/FilterQuery/FilterQueryPart.php rename src/{Filter => FilterQuery}/FilterQueryPartCollection.php (90%) rename src/{Filter/FilterQueryPart.php => FilterQuery/FilterQueryPartProcessor.php} (67%) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 1757d2d8..990b5568 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -15,7 +15,7 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; @@ -666,12 +666,16 @@ public function jsonSerialize() protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filterType) { + if (!$this->getData()[$config->getElementName()]) { + return; + } + $context = new FilterTypeContext(); $context->setId($config->id); $context->setName($config->getElementName()); $context->setField($config->field); $context->setOperator($config->operator); - $context->setValue($this->getData()[$context->getName()] ?: ''); + $context->setValue($this->getData()[$config->getElementName()] ?: ''); $context->setDefaultValue($config->defaultValue); $context->setParent($config->getRelated('pid')); $context->setSubmitOnChange($config->submitOnChange); diff --git a/src/Event/ModifyFilterQueryPartsEvent.php b/src/Event/ModifyFilterQueryPartsEvent.php index 07853b75..a2643366 100644 --- a/src/Event/ModifyFilterQueryPartsEvent.php +++ b/src/Event/ModifyFilterQueryPartsEvent.php @@ -8,7 +8,7 @@ namespace HeimrichHannot\FilterBundle\Event; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use Symfony\Component\EventDispatcher\Event; class ModifyFilterQueryPartsEvent extends Event diff --git a/src/Filter/FilterQueryPartProcessor.php b/src/Filter/FilterQueryPartProcessor.php deleted file mode 100644 index fb7677be..00000000 --- a/src/Filter/FilterQueryPartProcessor.php +++ /dev/null @@ -1,27 +0,0 @@ -connection = $connection; - } - - public function composeQueryPart(FilterTypeContext $context): FilterQueryPart - { - return new FilterQueryPart($context, $this->connection); - } -} diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php new file mode 100644 index 00000000..0e1c4999 --- /dev/null +++ b/src/FilterQuery/FilterQueryPart.php @@ -0,0 +1,84 @@ +name = $context->getName(); + $this->filterElementId = $context->getId(); + } + + public function getWildcard(): string + { + return $this->wildcard; + } + + public function setWildcard(string $wildcard): void + { + $this->wildcard = $wildcard; + } + + /** + * @return array|\DateTime|int|string + */ + public function getValue() + { + return $this->value; + } + + /** + * @param array|\DateTime|int|string $value + */ + public function setValue($value): void + { + $this->value = $value; + } + + public function getValueType() + { + return $this->valueType; + } + + public function setValueType($valueType): void + { + $this->valueType = $valueType; + } +} diff --git a/src/Filter/FilterQueryPartCollection.php b/src/FilterQuery/FilterQueryPartCollection.php similarity index 90% rename from src/Filter/FilterQueryPartCollection.php rename to src/FilterQuery/FilterQueryPartCollection.php index 11df09c6..437b861b 100644 --- a/src/Filter/FilterQueryPartCollection.php +++ b/src/FilterQuery/FilterQueryPartCollection.php @@ -5,7 +5,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\Filter; +namespace HeimrichHannot\FilterBundle\FilterQuery; class FilterQueryPartCollection diff --git a/src/Filter/FilterQueryPart.php b/src/FilterQuery/FilterQueryPartProcessor.php similarity index 67% rename from src/Filter/FilterQueryPart.php rename to src/FilterQuery/FilterQueryPartProcessor.php index 49c70b3a..031f2d3f 100644 --- a/src/Filter/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\Filter; +namespace HeimrichHannot\FilterBundle\FilterQuery; use Contao\Controller; use Doctrine\DBAL\Connection; @@ -15,68 +15,58 @@ use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; -class FilterQueryPart +class FilterQueryPartProcessor { - /** - * @var string - */ - public $name; - /** - * @var string - */ - public $query; - - /** - * @var string - */ - public $wildcard; - - /** - * @var string|int|array|\DateTime - */ - public $value; - - /** - * @var int|string|null - */ - public $valueType; - /** - * @var int - */ - protected $filterElementId; + protected Connection $connection; - /** - * @var Connection - */ - protected $connection; - - public function __construct(FilterTypeContext $context, Connection $connection) + public function __construct(Connection $connection) { $this->connection = $connection; - $this->name = $context->getName(); - $this->filterElementId = $context->getId(); - $this->query = $this->composeQuery($context); } - public function getFilterElementId(): int + public function composeQueryPart(FilterTypeContext $context): FilterQueryPart { - return $this->filterElementId; + $queryPart = new FilterQueryPart($context); + $queryPart->query = $this->composeQuery($context, $queryPart); + + return $queryPart; } - public function setFilterElementId(int $filterElementId): void + private function composeQuery(FilterTypeContext $filterTypeContext, FilterQueryPart $filterQueryPart): string { - $this->filterElementId = $filterElementId; + $options = [ + 'wildcardSuffix' => $filterTypeContext->getId(), + 'valueType' => null, + ]; + + if ($filterTypeContext->getValue() instanceof \DateTime) { + /** + * @var \DateTime + */ + $date = $filterTypeContext->getValue(); + + $filterTypeContext->setValue($date->getTimeStamp()); + $options['valueType'] = Types::INTEGER; + } + + return $this->composeWhereForQueryBuilder( + $filterTypeContext->getField(), + $filterTypeContext->getOperator(), + $filterTypeContext->getValue(), + $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], + $filterQueryPart, + $options + ); } /** * @param null $value - * @param array $options - * { - * wildcardSuffix: string, - * valueType: string + * @param array $options { + * wildcardSuffix: string, + * valueType: string * } */ - public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $dca = null, array $options = []): string + public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $dca = null, FilterQueryPart $filterQueryPart, array $options = []): string { $queryBuilder = new QueryBuilder($this->connection); @@ -92,49 +82,49 @@ public function composeWhereForQueryBuilder(string $field, string $operator, $va switch ($operator) { case DatabaseUtil::OPERATOR_LIKE: $where = $queryBuilder->expr()->like($field, $wildcard); - $this->applyParameterValues($wildcard, '%'.$value.'%', $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, '%'.$value.'%', $valueType); break; case DatabaseUtil::OPERATOR_UNLIKE: $where = $queryBuilder->expr()->notLike($field, $wildcard); - $this->applyParameterValues($wildcard, '%'.$value.'%', $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, '%'.$value.'%', $valueType); break; case DatabaseUtil::OPERATOR_EQUAL: $where = $queryBuilder->expr()->eq($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; case DatabaseUtil::OPERATOR_UNEQUAL: $where = $queryBuilder->expr()->neq($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; case DatabaseUtil::OPERATOR_LOWER: $where = $queryBuilder->expr()->lt($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; case DatabaseUtil::OPERATOR_LOWER_EQUAL: $where = $queryBuilder->expr()->lte($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; case DatabaseUtil::OPERATOR_GREATER: $where = $queryBuilder->expr()->gt($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; case DatabaseUtil::OPERATOR_GREATER_EQUAL: $where = $queryBuilder->expr()->gte($field, $wildcard); - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); break; @@ -152,7 +142,7 @@ function ($val) { }, $value ); - $this->applyParameterValues($wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + $this->applyParameterValues($filterQueryPart, $wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); } break; @@ -171,7 +161,7 @@ function ($val) { }, $value ); - $this->applyParameterValues($wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + $this->applyParameterValues($filterQueryPart, $wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); } break; @@ -205,6 +195,7 @@ function ($val) { if (\is_array($value)) { // build a regexp alternative, e.g. (:"1";|:"2";) $this->applyParameterValues( + $filterQueryPart, $wildcard, '('.implode( '|', @@ -218,11 +209,11 @@ function ($val) { $valueType ); } else { - $this->applyParameterValues($wildcard, ':"'.$value.'";', $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, ':"'.$value.'";', $valueType); } } else { // TODO: this makes no sense, yet - $this->applyParameterValues($wildcard, $value, $valueType); + $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); } break; @@ -231,72 +222,10 @@ function ($val) { return $where; } - public function applyParameterValues(string $wildcard, $value, $valueType): void - { - $this->setWildcard($wildcard); - $this->setValue($value); - $this->setValueType($valueType); - } - - public function getWildcard(): string + public function applyParameterValues(FilterQueryPart $filterQueryPart, string $wildcard, $value, $valueType): void { - return $this->wildcard; - } - - public function setWildcard(string $wildcard): void - { - $this->wildcard = $wildcard; - } - - /** - * @return array|\DateTime|int|string - */ - public function getValue() - { - return $this->value; - } - - /** - * @param array|\DateTime|int|string $value - */ - public function setValue($value): void - { - $this->value = $value; - } - - public function getValueType() - { - return $this->valueType; - } - - public function setValueType($valueType): void - { - $this->valueType = $valueType; - } - - private function composeQuery(FilterTypeContext $filterTypeContext): string - { - $options = [ - 'wildcardSuffix' => $filterTypeContext->getId(), - 'valueType' => null, - ]; - - if ($filterTypeContext->getValue() instanceof \DateTime) { - /** - * @var \DateTime - */ - $date = $filterTypeContext->getValue(); - - $filterTypeContext->setValue($date->getTimeStamp()); - $options['valueType'] = Types::INTEGER; - } - - return $this->composeWhereForQueryBuilder( - $filterTypeContext->getField(), - $filterTypeContext->getOperator(), - $filterTypeContext->getValue(), - $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], - $options - ); + $filterQueryPart->setWildcard($wildcard); + $filterQueryPart->setValue($value); + $filterQueryPart->setValueType($valueType); } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index ecd4f7fc..88b026ed 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -8,8 +8,8 @@ namespace HeimrichHannot\FilterBundle\FilterType; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Contracts\Translation\TranslatorInterface; diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index e25ad284..770a1ccf 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -10,8 +10,8 @@ use Doctrine\DBAL\Driver\Connection; use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index f7bae160..bf567f83 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -9,8 +9,8 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use Contao\Date; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection; -use HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 0ea5d741..9ac6ba50 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -187,8 +187,8 @@ services: huh.filter.filter_type.collection: '@HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection' - HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection: - huh.filter.filter_query_part_collection: '@HeimrichHannot\FilterBundle\Filter\FilterQueryPartCollection' + HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection: + huh.filter.filter_query_part_collection: '@HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection' - HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor: - huh.filter.filter_query_part_processor: '@HeimrichHannot\FilterBundle\Filter\FilterQueryPartProcessor' + HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor: + huh.filter.filter_query_part_processor: '@HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor' From 2d80b5efe546c227d8367459900e70adb028f7af Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Wed, 21 Apr 2021 12:25:25 +0200 Subject: [PATCH 21/58] added visual improvements to filterTypes --- src/Config/FilterConfig.php | 2 +- .../FilterConfigElementContainer.php | 29 +++++- src/FilterQuery/FilterQueryPartProcessor.php | 59 ++++++------ src/FilterType/AbstractFilterType.php | 28 ++++++ src/FilterType/FilterTypeContext.php | 90 +++++++++++++++++++ src/FilterType/Type/TextType.php | 19 +++- src/Form/FilterType.php | 14 ++- src/Model/FilterConfigElementModel.php | 3 + .../contao/dca/tl_filter_config_element.php | 8 +- 9 files changed, 209 insertions(+), 43 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 990b5568..d643a897 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -676,7 +676,7 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setField($config->field); $context->setOperator($config->operator); $context->setValue($this->getData()[$config->getElementName()] ?: ''); - $context->setDefaultValue($config->defaultValue); + $context->setDefaultValue($config->addDefaultValue ? $config->defaultValue : ''); $context->setParent($config->getRelated('pid')); $context->setSubmitOnChange($config->submitOnChange); $context->setExpanded($config->expanded); diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index bbdc0aba..b6db4080 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -14,6 +14,7 @@ use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\UtilsBundle\Choice\MessageChoice; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; @@ -34,14 +35,24 @@ class FilterConfigElementContainer /** * @var ContainerUtil */ - protected ContainerUtil $container; - - public function __construct(array $bundleConfig, TypeChoice $typeChoice, FilterTypeCollection $typeCollection, ContainerUtil $container) - { + protected $container; + /** + * @var MessageChoice + */ + protected $messageChoice; + + public function __construct( + array $bundleConfig, + TypeChoice $typeChoice, + FilterTypeCollection $typeCollection, + ContainerUtil $container, + MessageChoice $messageChoice + ) { $this->bundleConfig = $bundleConfig; $this->typeChoice = $typeChoice; $this->typeCollection = $typeCollection; $this->container = $container; + $this->messageChoice = $messageChoice; } public function onLoadCallback(DataContainer $dc): void @@ -110,4 +121,14 @@ public function getButtonTypes(DataContainer $dc): array { return ButtonType::BUTTON_TYPES; } + + public function getInputGroupAppendOptions(DataContainer $dc): array + { + return $this->messageChoice->getCachedChoices('huh.filter.input_group_text'); + } + + public function getInputGroupPrependOptions(DataContainer $dc): array + { + return $this->messageChoice->getCachedChoices('huh.filter.input_group_text'); + } } diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index 031f2d3f..d34d0209 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -32,38 +32,11 @@ public function composeQueryPart(FilterTypeContext $context): FilterQueryPart return $queryPart; } - private function composeQuery(FilterTypeContext $filterTypeContext, FilterQueryPart $filterQueryPart): string - { - $options = [ - 'wildcardSuffix' => $filterTypeContext->getId(), - 'valueType' => null, - ]; - - if ($filterTypeContext->getValue() instanceof \DateTime) { - /** - * @var \DateTime - */ - $date = $filterTypeContext->getValue(); - - $filterTypeContext->setValue($date->getTimeStamp()); - $options['valueType'] = Types::INTEGER; - } - - return $this->composeWhereForQueryBuilder( - $filterTypeContext->getField(), - $filterTypeContext->getOperator(), - $filterTypeContext->getValue(), - $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], - $filterQueryPart, - $options - ); - } - /** * @param null $value * @param array $options { - * wildcardSuffix: string, - * valueType: string + * wildcardSuffix: string, + * valueType: string * } */ public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $dca = null, FilterQueryPart $filterQueryPart, array $options = []): string @@ -212,7 +185,6 @@ function ($val) { $this->applyParameterValues($filterQueryPart, $wildcard, ':"'.$value.'";', $valueType); } } else { - // TODO: this makes no sense, yet $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); } @@ -228,4 +200,31 @@ public function applyParameterValues(FilterQueryPart $filterQueryPart, string $w $filterQueryPart->setValue($value); $filterQueryPart->setValueType($valueType); } + + private function composeQuery(FilterTypeContext $filterTypeContext, FilterQueryPart $filterQueryPart): string + { + $options = [ + 'wildcardSuffix' => $filterTypeContext->getId(), + 'valueType' => null, + ]; + + if ($filterTypeContext->getValue() instanceof \DateTime) { + /** + * @var \DateTime + */ + $date = $filterTypeContext->getValue(); + + $filterTypeContext->setValue($date->getTimeStamp()); + $options['valueType'] = Types::INTEGER; + } + + return $this->composeWhereForQueryBuilder( + $filterTypeContext->getField(), + $filterTypeContext->getOperator(), + $filterTypeContext->getValue(), + $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], + $filterQueryPart, + $options + ); + } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 88b026ed..53649f43 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -112,6 +112,34 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['attr']['class'] = $filterTypeContext->getCssClass(); } + if ($filterTypeContext->getDefaultValue()) { + $options['data'] = $filterTypeContext->getDefaultValue(); + } + + if ($filterTypeContext->hasInputGroup()) { + if ('' !== $filterTypeContext->getInputGroupPrepend()) { + $prepend = $filterTypeContext->getInputGroupPrepend(); + + if ($this->translator->getCatalogue()->has($prepend)) { + $prepend = $this->translator->trans($prepend, ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); + } + + $options['input_group_prepend'] = $prepend; + } + + if ('' !== $filterTypeContext->getInputGroupAppend()) { + $append = $filterTypeContext->getInputGroupAppend(); + + if ($this->translator->getCatalogue()->has($append)) { + $append = $this->translator->trans($append, ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); + } + + $options['input_group_append'] = $append; + } + } + + $options['block_name'] = $filterTypeContext->getName(); + return $options; } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index 650c3e90..ad2e7558 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -13,6 +13,11 @@ class FilterTypeContext implements \IteratorAggregate { + /** + * @var bool + */ + private $addInputGroup = false; + /** * @var string */ @@ -33,6 +38,11 @@ class FilterTypeContext implements \IteratorAggregate */ private $dateTimeFormat; + /** + * @var int + */ + private $debounce = 0; + /** * @var string|array */ @@ -68,6 +78,16 @@ class FilterTypeContext implements \IteratorAggregate */ private $initial = false; + /** + * @var string + */ + private $inputGroupAppend; + + /** + * @var string + */ + private $inputGroupPrepend; + /** * @var string */ @@ -118,11 +138,21 @@ class FilterTypeContext implements \IteratorAggregate */ private $submitOnChange = false; + /** + * @var bool + */ + private $submitOnInput = false; + /** * @var string */ private $title; + /** + * @var int + */ + private $threshold = 0; + /** * @var string|array|int */ @@ -404,4 +434,64 @@ public function setWidget(string $widget): void { $this->widget = $widget; } + + public function getThreshold(): int + { + return $this->threshold; + } + + public function setThreshold(int $threshold): void + { + $this->threshold = $threshold; + } + + public function getDebounce(): int + { + return $this->debounce; + } + + public function setDebounce(int $debounce): void + { + $this->debounce = $debounce; + } + + public function isSubmitOnInput(): bool + { + return $this->submitOnInput; + } + + public function setSubmitOnInput(bool $submitOnInput): void + { + $this->submitOnInput = $submitOnInput; + } + + public function hasInputGroup(): bool + { + return $this->addInputGroup; + } + + public function setInputGroup(bool $addInputGroup): void + { + $this->addInputGroup = $addInputGroup; + } + + public function getInputGroupAppend(): string + { + return $this->inputGroupAppend; + } + + public function setInputGroupAppend(string $inputGroupAppend): void + { + $this->inputGroupAppend = $inputGroupAppend; + } + + public function getInputGroupPrepend(): string + { + return $this->inputGroupPrepend; + } + + public function setInputGroupPrepend(string $inputGroupPrepend): void + { + $this->inputGroupPrepend = $inputGroupPrepend; + } } diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 19567118..c96464a8 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -38,7 +38,7 @@ public function buildForm(FilterTypeContext $filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,submitOnInput;{visualization_legend},addPlaceholder,addDefaultValue,customLabel,hideLabel,inputGroup;'.$appendPalette; } public function getInitialPalette(string $prependPalette, string $appendPalette): string @@ -56,8 +56,25 @@ public function getOperators(): array DatabaseUtil::OPERATOR_LOWER_EQUAL, DatabaseUtil::OPERATOR_IN, DatabaseUtil::OPERATOR_NOT_IN, + DatabaseUtil::OPERATOR_IS_NULL, + DatabaseUtil::OPERATOR_IS_NOT_NULL, + DatabaseUtil::OPERATOR_IS_EMPTY, + DatabaseUtil::OPERATOR_IS_NOT_EMPTY, ]; return array_values(array_diff(parent::getOperators(), $remove)); } + + public function getOptions(FilterTypeContext $filterTypeContext): array + { + $options = parent::getOptions($filterTypeContext); + + if ($filterTypeContext->isSubmitOnInput() && (bool) $filterTypeContext->getParent()->row()['asyncFormSubmit']) { + $options['attr']['data-submit-on-input'] = '1'; + $options['attr']['data-threshold'] = $filterTypeContext->getThreshold(); + $options['attr']['data-debounce'] = $filterTypeContext->getDebounce(); + } + + return $options; + } } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 79cc3b15..c6dbccfe 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -190,7 +190,7 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setId($element->id); $context->setName($element->getElementName()); $context->setValue($element->value); - $context->setDefaultValue($element->defaultValue); + $context->setDefaultValue($element->addDefaultValue ? $element->defaultValue : ''); $context->setPlaceholder($element->placeholder); $context->setFormBuilder($builder); $context->setTitle($element->title); @@ -208,6 +208,18 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $context->setHtml5($element->html5); $context->setWidget($element->dateWidget); + if ($element->submitOnInput) { + $context->setSubmitOnInput($element->submitOnInput); + $context->setThreshold($element->threshold); + $context->setDebounce($element->debounce); + } + + if ((bool) $element->inputGroup) { + $context->setInputGroup(true); + $context->setInputGroupAppend($element->inputGroupAppend); + $context->setInputGroupPrepend($element->inputGroupPrepend); + } + if ($element->hideLabel) { $context->hideLabel(); } diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index 030db2cb..50fa6e24 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -102,6 +102,9 @@ * @property string $stop * @property bool $useRangeSlider * @property string $buttonType + * @property string $threshold + * @property string $debounce + * @property bool $submitOnInput * * @method FilterConfigElementModel|null findById($id, array $opt = []) * @method FilterConfigElementModel|null findByPk($id, array $opt = []) diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 521ef2cb..e737d474 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -564,9 +564,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['inputGroupPrepend'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { - return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.input_group_text'); - }, + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getInputGroupPrependOptions'], 'eval' => ['chosen' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], 'sql' => "varchar(128) NOT NULL default ''", ], @@ -574,9 +572,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['inputGroupAppend'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { - return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.input_group_text'); - }, + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getInputGroupAppendOptions'], 'eval' => ['chosen' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], 'sql' => "varchar(128) NOT NULL default ''", ], From 153b4333e0d0c26c8047877e84dd6f1f3a20ce90 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 22 Apr 2021 13:31:04 +0200 Subject: [PATCH 22/58] refactoring and fixing datetimeTyp --- src/Config/FilterConfig.php | 9 +-- src/FilterQuery/FilterQueryPartProcessor.php | 16 +--- src/FilterType/FilterTypeContext.php | 17 ++++- src/FilterType/Type/DateTimeType.php | 79 ++++++++++++++------ src/Form/FilterType.php | 9 ++- 5 files changed, 87 insertions(+), 43 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index d643a897..eb421dac 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -675,8 +675,8 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context->setName($config->getElementName()); $context->setField($config->field); $context->setOperator($config->operator); - $context->setValue($this->getData()[$config->getElementName()] ?: ''); - $context->setDefaultValue($config->addDefaultValue ? $config->defaultValue : ''); + $context->setValue($this->getData()[$config->getElementName()]); + $context->setDefaultValue($config->addDefaultValue ?: $config->defaultValue); $context->setParent($config->getRelated('pid')); $context->setSubmitOnChange($config->submitOnChange); $context->setExpanded($config->expanded); @@ -737,15 +737,14 @@ protected function mapFormsToData() if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) { // If the field is of type DateTime and the data is the same skip the update to // keep the original object hash - if ($form->getData() instanceof \DateTime && $form->getData() === $propertyAccessor->getValue($data, + if ($form->getData() instanceof \DateTimeInterface && $form->getData() === $propertyAccessor->getValue($data, $propertyPath)) { continue; } // If the data is identical to the value in $data, we are // dealing with a reference - if (!\is_object($data) || !$config->getByReference() || $form->getData() !== $propertyAccessor->getValue($data, - $propertyPath)) { + if (!\is_object($data) || !$config->getByReference() || $form->getData() !== $propertyAccessor->getValue($data, $propertyPath)) { $propertyAccessor->setValue($data, $propertyPath, $form->getData()); } } diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index d34d0209..5f30e4fc 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -11,17 +11,19 @@ use Contao\Controller; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; -use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use HeimrichHannot\UtilsBundle\Date\DateUtil; class FilterQueryPartProcessor { protected Connection $connection; + protected DateUtil $dateUtil; - public function __construct(Connection $connection) + public function __construct(Connection $connection, DateUtil $dateUtil) { $this->connection = $connection; + $this->dateUtil = $dateUtil; } public function composeQueryPart(FilterTypeContext $context): FilterQueryPart @@ -208,16 +210,6 @@ private function composeQuery(FilterTypeContext $filterTypeContext, FilterQueryP 'valueType' => null, ]; - if ($filterTypeContext->getValue() instanceof \DateTime) { - /** - * @var \DateTime - */ - $date = $filterTypeContext->getValue(); - - $filterTypeContext->setValue($date->getTimeStamp()); - $options['valueType'] = Types::INTEGER; - } - return $this->composeWhereForQueryBuilder( $filterTypeContext->getField(), $filterTypeContext->getOperator(), diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index ad2e7558..a626610e 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -154,10 +154,15 @@ class FilterTypeContext implements \IteratorAggregate private $threshold = 0; /** - * @var string|array|int + * @var string|array|int|\DateTime|\Date */ private $value; + /** + * @var string + */ + private $valueType; + /** * @var string */ @@ -494,4 +499,14 @@ public function setInputGroupPrepend(string $inputGroupPrepend): void { $this->inputGroupPrepend = $inputGroupPrepend; } + + public function getValueType(): string + { + return $this->valueType; + } + + public function setValueType(string $valueType): void + { + $this->valueType = $valueType; + } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index bf567f83..eb8db149 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\FilterType\Type; use Contao\Date; +use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; @@ -22,6 +23,9 @@ class DateTimeType extends AbstractFilterType { const TYPE = 'date_time_type'; + const WIDGET_TYPE_CHOICE = 'choice'; + const WIDGET_TYPE_SINGLE_TEXT = 'single_text'; + /** * @var DateUtil */ @@ -44,13 +48,15 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { + $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getValue())); + $filterTypeContext->setValueType(Types::INTEGER); + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } public function buildForm($filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - $builder->add($filterTypeContext->getName(), SymfonyDateTimeType::class, $this->getOptions($filterTypeContext)); } @@ -82,36 +88,61 @@ public function getOptions(FilterTypeContext $filterTypeContext): array { $options = parent::getOptions($filterTypeContext); $format = $filterTypeContext->getDateTimeFormat() ?: 'd.m.Y H:i'; - $options['html5'] = $filterTypeContext->isHtml5(); - if (true === $options['html5']) { - $options['date_format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + $options['widget'] = $filterTypeContext->getWidget(); - if ($filterTypeContext->getMinDateTime()) { - $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used - } + switch ($filterTypeContext->getWidget()) { + case static::WIDGET_TYPE_SINGLE_TEXT: + if ($filterTypeContext->isHtml5()) { + $options['html5'] = $filterTypeContext->isHtml5(); + $options['date_format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); - if ($filterTypeContext->getMaxDateTime()) { - $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used - } - } else { - $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); - } + if ($filterTypeContext->getMinDateTime()) { + $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + } - $options['group_attr']['class'] = isset($options['group_attr']['class']) ? $options['group_attr']['class'].' datepicker timepicker' : 'datepicker timepicker'; - $options['attr']['data-iso8601-format'] = $this->dateUtil->transformPhpDateFormatToISO8601($format); - $options['attr']['data-enable-time'] = 'true'; - $options['attr']['data-date-format'] = $format; + if ($filterTypeContext->getMaxDateTime()) { + $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + } + } else { + $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + } - if ($filterTypeContext->getMinDateTime()) { - $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); - } + $options['group_attr']['class'] = isset($options['group_attr']['class']) ? $options['group_attr']['class'].' datepicker timepicker' : 'datepicker timepicker'; + $options['attr']['data-iso8601-format'] = $this->dateUtil->transformPhpDateFormatToISO8601($format); + $options['attr']['data-enable-time'] = 'true'; + $options['attr']['data-date-format'] = $format; - if ($filterTypeContext->getMaxDateTime()) { - $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); - } + if ($filterTypeContext->getMinDateTime()) { + $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); + } - $options['widget'] = $filterTypeContext->getWidget(); + if ($filterTypeContext->getMaxDateTime()) { + $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); + } + + break; + + case static::WIDGET_TYPE_CHOICE: + // months and days restriction cant be configured from min and max date + + $time = time(); + + $minYear = Date::parse('Y', strtotime('-5 year', $time)); + $maxYear = Date::parse('Y', strtotime('+5 year', $time)); + + if ($filterTypeContext->getMinDateTime()) { + $minYear = Date::parse('Y', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); + } + + if ($filterTypeContext->getMaxDateTime()) { + $maxYear = Date::parse('Y', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); + } + + $options['years'] = range($minYear, $maxYear, 1); + + break; + } return $options; } diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index c6dbccfe..2477f2c5 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -21,6 +21,7 @@ use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -186,10 +187,16 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil return; } + $request = Request::createFromGlobals(); + $context = new FilterTypeContext(); + + if (null !== $request->query->get($element->getRelated('pid')->name)[$element->getElementName()]) { + $context->setValue($request->query->get($element->getRelated('pid')->name)[$element->getElementName()]); + } + $context->setId($element->id); $context->setName($element->getElementName()); - $context->setValue($element->value); $context->setDefaultValue($element->addDefaultValue ? $element->defaultValue : ''); $context->setPlaceholder($element->placeholder); $context->setFormBuilder($builder); From 5085895bac30f26c796f847a6392a0fd371bb990 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 22 Apr 2021 15:30:52 +0200 Subject: [PATCH 23/58] fixed code style --- src/DataContainer/FilterConfigElementContainer.php | 12 ++++++------ .../contao/dca/tl_filter_config_element.php | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index b6db4080..67a1c94e 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -71,7 +71,7 @@ public function onLoadCallback(DataContainer $dc): void } } - public function getTypeOptions(DataContainer $dc) + public function onTypeOptionsCallback(DataContainer $dc) { if (!$this->bundleConfig['filter']['disable_legacy_filters']) { return $this->typeChoice->getCachedChoices($dc); @@ -92,7 +92,7 @@ public function getTypeOptions(DataContainer $dc) return $options; } - public function getOperators(DataContainer $dc) + public function onOperatorOptionsCallback(DataContainer $dc) { if (!$this->bundleConfig['filter']['disable_legacy_filters']) { return DatabaseUtil::OPERATORS; @@ -101,7 +101,7 @@ public function getOperators(DataContainer $dc) return $this->typeCollection->getType($dc->activeRecord->type)->getOperators(); } - public function getDateWidgetOptions(DataContainer $dc): array + public function onDateWidgetOptionsCallback(DataContainer $dc): array { if ($this->bundleConfig['filter']['disable_legacy_filers']) { return [ @@ -117,17 +117,17 @@ public function getDateWidgetOptions(DataContainer $dc): array ]; } - public function getButtonTypes(DataContainer $dc): array + public function onButtonTypeOptionsCallback(DataContainer $dc): array { return ButtonType::BUTTON_TYPES; } - public function getInputGroupAppendOptions(DataContainer $dc): array + public function onInputGroupAppendOptionsCallback(DataContainer $dc): array { return $this->messageChoice->getCachedChoices('huh.filter.input_group_text'); } - public function getInputGroupPrependOptions(DataContainer $dc): array + public function onInputGroupPrependOptionsCallback(DataContainer $dc): array { return $this->messageChoice->getCachedChoices('huh.filter.input_group_text'); } diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index e737d474..4b02c136 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -217,7 +217,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getTypeOptions'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onTypeOptionsCallback'], 'reference' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['reference']['type'], 'eval' => [ 'chosen' => true, @@ -357,7 +357,7 @@ 'operator' => [ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['operator'], 'inputType' => 'select', - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getOperators'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onOperatorOptionsCallback'], 'reference' => &$GLOBALS['TL_LANG']['MSC']['databaseOperators'], 'eval' => ['tl_class' => 'w50', 'mandatory' => true, 'includeBlankOption' => true], 'sql' => "varchar(16) NOT NULL default ''", @@ -528,7 +528,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_SINGLE_TEXT, - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getDateWidgetOptions'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onDateWidgetOptionsCallback'], 'eval' => ['tl_class' => 'w50', 'chosen' => true, 'submitOnChange' => true], 'sql' => "varchar(16) NOT NULL default ''", ], @@ -564,7 +564,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['inputGroupPrepend'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getInputGroupPrependOptions'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onInputGroupPrependOptionsCallback'], 'eval' => ['chosen' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], 'sql' => "varchar(128) NOT NULL default ''", ], @@ -572,7 +572,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['inputGroupAppend'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getInputGroupAppendOptions'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onInputGroupAppendOptionsCallback'], 'eval' => ['chosen' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], 'sql' => "varchar(128) NOT NULL default ''", ], @@ -887,7 +887,7 @@ 'exclude' => true, 'search' => true, 'inputType' => 'select', - 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'getButtonTypes'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onButtonTypeOptionsCallback'], 'eval' => [ 'tl_class' => 'w50', 'mandatory' => true, From 33c4fcc035d11c38661e66d5ae9784ac2c3f5df7 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 22 Apr 2021 15:55:49 +0200 Subject: [PATCH 24/58] fixed datetime format issue, to break form on wrong data value --- src/FilterType/Type/DateTimeType.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index eb8db149..d2e14cc8 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -144,6 +144,13 @@ public function getOptions(FilterTypeContext $filterTypeContext): array break; } + //TODO: double check if correct + if (empty($filterTypeContext->getValue())) { + $options['data'] = null; + } else { + $options['data'] = date_create_from_format($filterTypeContext->getDateTimeFormat(), $filterTypeContext->getValue()); + } + return $options; } } From 4b06c14a9fd8f38d52c6c9188bd74c43b9fc470b Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 22 Apr 2021 16:16:12 +0200 Subject: [PATCH 25/58] fixed data option for choiceType --- src/FilterType/Type/ChoiceType.php | 3 ++- src/FilterType/Type/DateTimeType.php | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index 770a1ccf..670ecee7 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -48,7 +48,6 @@ public static function getType(): string public function buildForm($filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - $builder->add($filterTypeContext->getName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); } @@ -111,6 +110,8 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; } + $options['data'] = $filterTypeContext->getValue(); + return $options; } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index d2e14cc8..5fdcfe51 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -14,12 +14,13 @@ use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Date\DateUtil; use Symfony\Component\Form\Extension\Core\Type\DateTimeType as SymfonyDateTimeType; use Symfony\Contracts\Translation\TranslatorInterface; -class DateTimeType extends AbstractFilterType +class DateTimeType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'date_time_type'; @@ -65,6 +66,11 @@ public function getPalette(string $prependPalette, string $appendPalette): strin return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;{visualization_legend},html5,dateWidget,customLabel,hideLabel,addPlaceholder;'.$appendPalette; } + public function getInitialPalette(string $prependPalette, string $appendPalette) + { + return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;'.$appendPalette; + } + public function getOperators(): array { //remove this operators from the DatabaseUtil::OPERATORS array From e4918871f1123e2034f1cabf7a6f4280d44a3a63 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 22 Apr 2021 16:31:04 +0200 Subject: [PATCH 26/58] fixed code style --- .../FilterConfigElementContainer.php | 9 +++++++ src/FilterType/AbstractFilterType.php | 24 ------------------- src/FilterType/Type/ChoiceType.php | 4 ++-- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 67a1c94e..6468c1d5 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -101,6 +101,15 @@ public function onOperatorOptionsCallback(DataContainer $dc) return $this->typeCollection->getType($dc->activeRecord->type)->getOperators(); } + public function onPlaceholderOptionsCallback(DataContainer $dc): array + { + if (!$this->bundleConfig['filter']['disable_legacy_filers']) { + return $this->messageChoice->getCachedChoices('huh.filter.placeholder'); + } + + return $this->typeCollection->getType($dc->activeRecord->type)->getPlaceholders(); + } + public function onDateWidgetOptionsCallback(DataContainer $dc): array { if ($this->bundleConfig['filter']['disable_legacy_filers']) { diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 53649f43..3308318a 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -32,11 +32,6 @@ abstract class AbstractFilterType implements FilterTypeInterface */ protected $translator; - /** - * @var FilterTypeContext - */ - private $context; - /** * @var string */ @@ -53,20 +48,6 @@ public function __construct( $this->translator = $translator; } - public function getContext(): FilterTypeContext - { - if (!isset($this->context)) { - $this->setDefaultContext(); - } - - return $this->context; - } - - public function setContext(FilterTypeContext $context) - { - $this->context = $context; - } - public function getPalette(string $prependPalette, string $appendPalette): string { return $prependPalette.$appendPalette; @@ -151,9 +132,4 @@ protected function initialize(): void $this->setGroup(static::GROUP); } } - - private function setDefaultContext() - { - $this->context = new FilterTypeContext(); - } } diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index 670ecee7..caa12b80 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -105,13 +105,13 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['multiple'] = $filterTypeContext->isMultiple(); + $options['data'] = $filterTypeContext->getValue(); + // forgiving array handling if (true === $filterTypeContext->isMultiple() && isset($options['data'])) { $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; } - $options['data'] = $filterTypeContext->getValue(); - return $options; } From 58cce7e6f99057da547fb85abc455ed0255734ab Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 23 Apr 2021 11:59:43 +0200 Subject: [PATCH 27/58] refactored FilterTypeContext, FilterQueryPart, FilterQueryProcessor, extended Event --- README.md | 4 +- src/Config/FilterConfig.php | 35 +- src/ContaoManager/Plugin.php | 1 - .../FilterConfigElementContainer.php | 6 +- src/Event/ModifyFilterQueryPartsEvent.php | 19 +- src/FilterQuery/FilterQueryPart.php | 80 +++- src/FilterQuery/FilterQueryPartCollection.php | 9 +- src/FilterQuery/FilterQueryPartProcessor.php | 118 ++--- src/FilterType/AbstractFilterType.php | 64 ++- src/FilterType/FilterTypeContext.php | 441 +----------------- src/FilterType/FilterTypeInterface.php | 2 + src/FilterType/Type/ButtonType.php | 5 +- src/FilterType/Type/ChoiceType.php | 37 +- src/FilterType/Type/DateTimeType.php | 41 +- src/FilterType/Type/TextType.php | 31 +- src/Form/FilterType.php | 41 +- src/Resources/config/config.yml | 2 +- src/Resources/config/filter.yml | 4 - src/Resources/config/services.yml | 8 +- 19 files changed, 273 insertions(+), 675 deletions(-) delete mode 100644 src/Resources/config/filter.yml diff --git a/README.md b/README.md index 5ba0fb28..f5a6338e 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ This bundle offers a generic filter module to use with arbitrary contao entities - Label/Message handling using symfony translations - Render form always empty (without user selection) - Merge data over multiple filter forms with same form name -- Default Values (can be overwritten by user) -- Initial Values (can`t be overwritten by user) +- Default Values +- Initial Values - Stores filter data in session (no GET parameter URL remnant) - Content element "Filter-Preselect" with optional redirect functionality to preselect filter on given page - Content element "Filter-Hyperlink" with filter preselect feature diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index eb421dac..7a6288b8 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -16,7 +16,8 @@ use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; @@ -107,6 +108,11 @@ class FilterConfig implements \JsonSerializable */ protected $eventDispatcher; + /** + * @var FilterQueryPartProcessor + */ + protected $filterQueryPartProcessor; + /** * @var ContainerInterface */ @@ -127,7 +133,8 @@ public function __construct( Connection $connection, RequestStack $requestStack, FilterQueryPartCollection $filterQueryPartCollection, - EventDispatcherInterface $eventDispatcher + EventDispatcherInterface $eventDispatcher, + FilterQueryPartProcessor $filterQueryPartProcessor ) { $this->framework = $framework; $this->session = $session; @@ -136,6 +143,7 @@ public function __construct( $this->requestStack = $requestStack; $this->filterQueryPartCollection = $filterQueryPartCollection; $this->eventDispatcher = $eventDispatcher; + $this->filterQueryPartProcessor = $filterQueryPartProcessor; } /** @@ -246,7 +254,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $types = $this->container->get('huh.filter.choice.type')->getCachedChoices(); - $newTypes = \System::getContainer()->get('huh.filter.filter_type.collection')->getTypes(); + $newTypes = \System::getContainer()->get(FilterTypeCollection::class)->getTypes(); $types = array_merge($types, $newTypes); if (!\is_array($types) || empty($types)) { @@ -264,7 +272,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip continue; } - if (!\is_array($types[$element->type]) && $types[$element->type] instanceof AbstractFilterType) { + if (!\is_array($types[$element->type]) && $types[$element->type] instanceof FilterTypeInterface) { $this->processFilterType($element, $types[$element->type]); continue; @@ -291,13 +299,13 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip //apply parts from FilterQueryPartCollection /** @noinspection PhpMethodParametersCountMismatchInspection */ /** @noinspection PhpParamsInspection */ - $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection)); + $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection, $this->getFilter())); /* * @var FilterQueryPart */ foreach ($event->getPartsCollection()->getParts() as $part) { - $this->queryBuilder->andWhere($part->query); + $this->queryBuilder->andWhere($this->filterQueryPartProcessor->composeWhereForQueryBuilder($part, $this->queryBuilder)); $this->queryBuilder->setParameter( $part->getWildcard(), $part->getValue(), @@ -671,20 +679,9 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp } $context = new FilterTypeContext(); - $context->setId($config->id); - $context->setName($config->getElementName()); - $context->setField($config->field); - $context->setOperator($config->operator); $context->setValue($this->getData()[$config->getElementName()]); - $context->setDefaultValue($config->addDefaultValue ?: $config->defaultValue); + $context->setElementConfig($config); $context->setParent($config->getRelated('pid')); - $context->setSubmitOnChange($config->submitOnChange); - $context->setExpanded($config->expanded); - $context->setMultiple($config->multiple); - $context->setDateTimeFormat($config->dateTimeFormat); - $context->setMinDateTime($config->minDateTime); - $context->setMaxDateTime($config->maxDateTime); - $context->setCustomLabel($config->customLabel); $filterType->buildQuery($context); } @@ -720,6 +717,8 @@ protected function mapFormsToData() } catch (TransformationFailedException $e) { $this->resetData(); $this->builder->setData($this->getData()); + + return; $forms = $this->builder->getForm(); } diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index cac51aaa..4de219d9 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -83,6 +83,5 @@ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInte public function registerContainerConfiguration(LoaderInterface $loader, array $managerConfig) { $loader->load('@HeimrichHannotContaoFilterBundle/Resources/config/datacontainer.yml'); - $loader->load('@HeimrichHannotContaoFilterBundle/Resources/config/filter.yml'); } } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 6468c1d5..76302e98 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -73,11 +73,7 @@ public function onLoadCallback(DataContainer $dc): void public function onTypeOptionsCallback(DataContainer $dc) { - if (!$this->bundleConfig['filter']['disable_legacy_filters']) { - return $this->typeChoice->getCachedChoices($dc); - } - - $options = []; + $options = $this->typeChoice->getCachedChoices($dc); foreach ($this->typeCollection->getTypes() as $key => $type) { $group = $type->getGroup(); diff --git a/src/Event/ModifyFilterQueryPartsEvent.php b/src/Event/ModifyFilterQueryPartsEvent.php index a2643366..0fd2c9cf 100644 --- a/src/Event/ModifyFilterQueryPartsEvent.php +++ b/src/Event/ModifyFilterQueryPartsEvent.php @@ -14,14 +14,21 @@ class ModifyFilterQueryPartsEvent extends Event { public const NAME = 'huh.filter.modify_filter_query_parts_event'; + /** * @var FilterQueryPartCollection */ protected $partsCollection; - public function __construct(FilterQueryPartCollection $partsCollection) + /** + * @var array|null + */ + protected $filter; + + public function __construct(FilterQueryPartCollection $partsCollection, ?array $filter) { $this->partsCollection = $partsCollection; + $this->filter = $filter; } public function getPartsCollection(): FilterQueryPartCollection @@ -33,4 +40,14 @@ public function setPartsCollection(FilterQueryPartCollection $partsCollection): { $this->partsCollection = $partsCollection; } + + public function getFilter(): ?array + { + return $this->filter; + } + + public function setFilter(array $filter): void + { + $this->filter = $filter; + } } diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php index 0e1c4999..f4f284d2 100644 --- a/src/FilterQuery/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPart.php @@ -15,35 +15,49 @@ class FilterQueryPart /** * @var string */ - public $name; + private $field; + + /** + * @var int + */ + private $filterElementId; + /** * @var string */ - public $query; + private $name; /** * @var string */ - public $wildcard; + private $operator; /** * @var string|int|array|\DateTime */ - public $value; + private $value; /** * @var int|string|null */ - public $valueType; + private $valueType; + /** - * @var int + * @var string */ - protected $filterElementId; + private $wildcard; - public function __construct(FilterTypeContext $context) + public function __construct(FilterTypeContext $filterTypeContext) { - $this->name = $context->getName(); - $this->filterElementId = $context->getId(); + $elementConfig = $filterTypeContext->getElementConfig(); + + $this->name = $elementConfig->getElementName(); + $this->filterElementId = $elementConfig->id; + $this->operator = $elementConfig->operator; + $this->field = $elementConfig->field; + $this->value = $filterTypeContext->getValue(); + $this->valueType = $filterTypeContext->getValueType(); + $this->wildcard = ':'.str_replace('.', '_', $elementConfig->field).'_'.$elementConfig->id; } public function getWildcard(): string @@ -72,13 +86,59 @@ public function setValue($value): void $this->value = $value; } + /** + * @return int|string|null + */ public function getValueType() { return $this->valueType; } + /** + * @param $valueType + */ public function setValueType($valueType): void { $this->valueType = $valueType; } + + public function getField(): string + { + return $this->field; + } + + public function setField(string $field): void + { + $this->field = $field; + } + + public function getFilterElementId(): int + { + return $this->filterElementId; + } + + public function setFilterElementId(int $filterElementId): void + { + $this->filterElementId = $filterElementId; + } + + public function getName(): string + { + return $this->name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function getOperator(): string + { + return $this->operator; + } + + public function setOperator(string $operator): void + { + $this->operator = $operator; + } } diff --git a/src/FilterQuery/FilterQueryPartCollection.php b/src/FilterQuery/FilterQueryPartCollection.php index 437b861b..26154faa 100644 --- a/src/FilterQuery/FilterQueryPartCollection.php +++ b/src/FilterQuery/FilterQueryPartCollection.php @@ -1,5 +1,6 @@ parts[$part->name] = $part; + $this->parts[$part->getName()] = $part; } public function removePartByName(string $name): void { unset($this->parts[$name]); } - -} \ No newline at end of file +} diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index 5f30e4fc..7426386a 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -26,80 +26,67 @@ public function __construct(Connection $connection, DateUtil $dateUtil) $this->dateUtil = $dateUtil; } - public function composeQueryPart(FilterTypeContext $context): FilterQueryPart + public function composeQueryPart(FilterTypeContext $filterTypeContext): FilterQueryPart { - $queryPart = new FilterQueryPart($context); - $queryPart->query = $this->composeQuery($context, $queryPart); - - return $queryPart; + return new FilterQueryPart($filterTypeContext); } - /** - * @param null $value - * @param array $options { - * wildcardSuffix: string, - * valueType: string - * } - */ - public function composeWhereForQueryBuilder(string $field, string $operator, $value, array $dca = null, FilterQueryPart $filterQueryPart, array $options = []): string + public function composeWhereForQueryBuilder(FilterQueryPart $filterQueryPart, QueryBuilder $queryBuilder, array $dca = []): string { - $queryBuilder = new QueryBuilder($this->connection); - - $valueType = $options['valueType'] ?? null; - $wildcardSuffix = $options['wildcardSuffix'] ?? ''; - $wildcard = ':'.str_replace('.', '_', $field).$wildcardSuffix; $where = ''; + $value = $filterQueryPart->getValue(); + if (\is_string($value)) { $value = Controller::replaceInsertTags(\is_array($value) ? implode(' ', $value) : $value, false); } - switch ($operator) { + switch ($filterQueryPart->getOperator()) { case DatabaseUtil::OPERATOR_LIKE: - $where = $queryBuilder->expr()->like($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, '%'.$value.'%', $valueType); + $where = $queryBuilder->expr()->like($filterQueryPart->getField(), $filterQueryPart->getWildcard()); + + if (false === strpos($value, '%')) { + $filterQueryPart->setValue('%'.$value.'%'); + } break; case DatabaseUtil::OPERATOR_UNLIKE: - $where = $queryBuilder->expr()->notLike($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, '%'.$value.'%', $valueType); + $where = $queryBuilder->expr()->notLike($filterQueryPart->getField(), $filterQueryPart->getWildcard()); + + if (false === strpos($value, '%')) { + $filterQueryPart->setValue('%'.$value.'%'); + } break; case DatabaseUtil::OPERATOR_EQUAL: - $where = $queryBuilder->expr()->eq($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->eq($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; case DatabaseUtil::OPERATOR_UNEQUAL: - $where = $queryBuilder->expr()->neq($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->neq($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; case DatabaseUtil::OPERATOR_LOWER: - $where = $queryBuilder->expr()->lt($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->lt($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; case DatabaseUtil::OPERATOR_LOWER_EQUAL: - $where = $queryBuilder->expr()->lte($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->lte($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; case DatabaseUtil::OPERATOR_GREATER: - $where = $queryBuilder->expr()->gt($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->gt($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; case DatabaseUtil::OPERATOR_GREATER_EQUAL: - $where = $queryBuilder->expr()->gte($field, $wildcard); - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); + $where = $queryBuilder->expr()->gte($filterQueryPart->getField(), $filterQueryPart->getWildcard()); break; @@ -110,14 +97,16 @@ public function composeWhereForQueryBuilder(string $field, string $operator, $va if (empty($value)) { $where = $queryBuilder->expr()->eq(1, 2); } else { - $where = $queryBuilder->expr()->in($field, $wildcard); + $where = $queryBuilder->expr()->in($filterQueryPart->getField(), $filterQueryPart->getWildcard()); $preparedValue = array_map( function ($val) { return addslashes(Controller::replaceInsertTags(trim($val), false)); }, $value ); - $this->applyParameterValues($filterQueryPart, $wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + + $filterQueryPart->setValue($preparedValue); + $filterQueryPart->setValueType(Connection::PARAM_STR_ARRAY); } break; @@ -129,49 +118,50 @@ function ($val) { if (empty($value)) { $where = $queryBuilder->expr()->eq(1, 2); } else { - $where = $queryBuilder->expr()->notIn($field, $wildcard); + $where = $queryBuilder->expr()->notIn($filterQueryPart->getField(), $filterQueryPart->getWildcard()); $preparedValue = array_map( function ($val) { return addslashes(Controller::replaceInsertTags(trim($val), false)); }, $value ); - $this->applyParameterValues($filterQueryPart, $wildcard, $preparedValue, Connection::PARAM_STR_ARRAY); + + $filterQueryPart->setValue($preparedValue); + $filterQueryPart->setValueType(Connection::PARAM_STR_ARRAY); } break; case DatabaseUtil::OPERATOR_IS_NULL: - $where = $queryBuilder->expr()->isNull($field); + $where = $queryBuilder->expr()->isNull($filterQueryPart->getField()); break; case DatabaseUtil::OPERATOR_IS_NOT_NULL: - $where = $queryBuilder->expr()->isNotNull($field); + $where = $queryBuilder->expr()->isNotNull($filterQueryPart->getField()); break; case DatabaseUtil::OPERATOR_IS_EMPTY: - $where = $queryBuilder->expr()->eq($field, '\'\''); + $where = $queryBuilder->expr()->eq($filterQueryPart->getField(), '\'\''); break; case DatabaseUtil::OPERATOR_IS_NOT_EMPTY: - $where = $queryBuilder->expr()->neq($field, '\'\''); + $where = $queryBuilder->expr()->neq($filterQueryPart->getField(), '\'\''); break; case DatabaseUtil::OPERATOR_REGEXP: case DatabaseUtil::OPERATOR_NOT_REGEXP: - $where = $field.(DatabaseUtil::OPERATOR_NOT_REGEXP == $operator ? ' NOT REGEXP ' : ' REGEXP ').$wildcard; + $where = $filterQueryPart->getField().(DatabaseUtil::OPERATOR_NOT_REGEXP == $filterQueryPart->getOperator() ? ' NOT REGEXP ' : ' REGEXP ').$filterQueryPart->getWildcard(); if (\is_array($dca) && isset($dca['eval']['multiple']) && $dca['eval']['multiple']) { // match a serialized blob if (\is_array($value)) { // build a regexp alternative, e.g. (:"1";|:"2";) - $this->applyParameterValues( - $filterQueryPart, - $wildcard, + + $preparedValue = '('.implode( '|', array_map( @@ -180,14 +170,12 @@ function ($val) { }, $value ) - ).')', - $valueType - ); + ).')'; + + $filterQueryPart->setValue($preparedValue); } else { - $this->applyParameterValues($filterQueryPart, $wildcard, ':"'.$value.'";', $valueType); + $filterQueryPart->setValue(':"'.$value.'";'); } - } else { - $this->applyParameterValues($filterQueryPart, $wildcard, $value, $valueType); } break; @@ -195,28 +183,4 @@ function ($val) { return $where; } - - public function applyParameterValues(FilterQueryPart $filterQueryPart, string $wildcard, $value, $valueType): void - { - $filterQueryPart->setWildcard($wildcard); - $filterQueryPart->setValue($value); - $filterQueryPart->setValueType($valueType); - } - - private function composeQuery(FilterTypeContext $filterTypeContext, FilterQueryPart $filterQueryPart): string - { - $options = [ - 'wildcardSuffix' => $filterTypeContext->getId(), - 'valueType' => null, - ]; - - return $this->composeWhereForQueryBuilder( - $filterTypeContext->getField(), - $filterTypeContext->getOperator(), - $filterTypeContext->getValue(), - $GLOBALS['TL_DCA'][$filterTypeContext->getParent()->row()['dataContainer']]['fields'][$filterTypeContext->getField()], - $filterQueryPart, - $options - ); - } } diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 3308318a..4aae6057 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -65,7 +65,22 @@ public function setGroup(string $group): void public function getOperators(): array { - return DatabaseUtil::OPERATORS; + return [ + DatabaseUtil::OPERATOR_LIKE, + DatabaseUtil::OPERATOR_UNLIKE, + DatabaseUtil::OPERATOR_EQUAL, + DatabaseUtil::OPERATOR_UNEQUAL, + DatabaseUtil::OPERATOR_LOWER, + DatabaseUtil::OPERATOR_LOWER_EQUAL, + DatabaseUtil::OPERATOR_GREATER, + DatabaseUtil::OPERATOR_GREATER_EQUAL, + DatabaseUtil::OPERATOR_IN, + DatabaseUtil::OPERATOR_NOT_IN, + DatabaseUtil::OPERATOR_IS_NULL, + DatabaseUtil::OPERATOR_IS_NOT_NULL, + DatabaseUtil::OPERATOR_REGEXP, + DatabaseUtil::OPERATOR_NOT_REGEXP, + ]; } public function buildQuery(FilterTypeContext $filterTypeContext) @@ -75,51 +90,50 @@ public function buildQuery(FilterTypeContext $filterTypeContext) public function getOptions(FilterTypeContext $filterTypeContext): array { + $elementConfig = $filterTypeContext->getElementConfig(); $options = []; - $options['label'] = $filterTypeContext->isCustomLabel() ? $filterTypeContext->getLabel() : $filterTypeContext->getTitle(); + $options['label'] = $elementConfig->customLabel ? $elementConfig->label : $elementConfig->title; // sr-only style for non-bootstrap projects is shipped within the filter_form_* templates - if (true === $filterTypeContext->isLabelHidden()) { + if (true === (bool) $elementConfig->hideLabel) { $options['label_attr'] = ['class' => 'sr-only']; } // always label for screen readers - $options['attr']['aria-label'] = $this->translator->trans($filterTypeContext->isCustomLabel() ? $filterTypeContext->getLabel() : $filterTypeContext->getTitle()); + $options['attr']['aria-label'] = $this->translator->trans($elementConfig->customLabel ? $elementConfig->label : $elementConfig->title); - if ($filterTypeContext->getPlaceholder()) { - $options['attr']['placeholder'] = $this->translator->trans($filterTypeContext->getPlaceholder(), ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); + if ((bool) $elementConfig->addPlaceholder) { + $options['attr']['placeholder'] = $this->translator->trans($elementConfig->placeholder, ['%label%' => $this->translator->trans($options['label']) ?: $elementConfig->title]); } - if ($filterTypeContext->getCssClass()) { - $options['attr']['class'] = $filterTypeContext->getCssClass(); + if (!empty($elementConfig->cssClass)) { + $options['attr']['class'] = $elementConfig->cssClass; } - if ($filterTypeContext->getDefaultValue()) { - $options['data'] = $filterTypeContext->getDefaultValue(); + if ((bool) $elementConfig->addDefaultValue) { + $options['data'] = $elementConfig->defaultValue; } - if ($filterTypeContext->hasInputGroup()) { - if ('' !== $filterTypeContext->getInputGroupPrepend()) { - $prepend = $filterTypeContext->getInputGroupPrepend(); + if ((bool) $elementConfig->inputGroup && !empty($elementConfig->inputGroupPrepend)) { + $prepend = $elementConfig->inputGroupPrepend; - if ($this->translator->getCatalogue()->has($prepend)) { - $prepend = $this->translator->trans($prepend, ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); - } - - $options['input_group_prepend'] = $prepend; + if ($this->translator->getCatalogue()->has($prepend)) { + $prepend = $this->translator->trans($prepend, ['%label%' => $this->translator->trans($options['label']) ?: $elementConfig->title]); } - if ('' !== $filterTypeContext->getInputGroupAppend()) { - $append = $filterTypeContext->getInputGroupAppend(); + $options['input_group_prepend'] = $prepend; + } - if ($this->translator->getCatalogue()->has($append)) { - $append = $this->translator->trans($append, ['%label%' => $this->translator->trans($options['label']) ?: $filterTypeContext->getTitle()]); - } + if ((bool) $elementConfig->inputGroup && !empty($elementConfig->inputGroupAppend)) { + $append = $elementConfig->inputGroupAppend; - $options['input_group_append'] = $append; + if ($this->translator->getCatalogue()->has($append)) { + $append = $this->translator->trans($append, ['%label%' => $this->translator->trans($options['label']) ?: $elementConfig->title]); } + + $options['input_group_append'] = $append; } - $options['block_name'] = $filterTypeContext->getName(); + $options['block_name'] = $elementConfig->getElementName(); return $options; } diff --git a/src/FilterType/FilterTypeContext.php b/src/FilterType/FilterTypeContext.php index a626610e..787ed70e 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/FilterType/FilterTypeContext.php @@ -9,225 +9,53 @@ namespace HeimrichHannot\FilterBundle\FilterType; use Contao\Model; +use DateTimeInterface; +use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; -class FilterTypeContext implements \IteratorAggregate +class FilterTypeContext { /** - * @var bool + * @var FilterConfigElementModel */ - private $addInputGroup = false; - - /** - * @var string - */ - private $buttonType; - - /** - * @var string - */ - private $cssClass; - - /** - * @var bool - */ - private $customLabel = false; - - /** - * @var string - */ - private $dateTimeFormat; - - /** - * @var int - */ - private $debounce = 0; - - /** - * @var string|array - */ - private $defaultValue; - - /** - * @var bool - */ - private $expanded = false; - - /** - * @var string - */ - private $field = ''; + private $elementConfig; /** * @var FormBuilderInterface */ private $formBuilder; - /** - * @var bool - */ - private $html5 = false; - - /** - * @var int - */ - private $id; - - /** - * @var bool - */ - private $initial = false; - - /** - * @var string - */ - private $inputGroupAppend; - - /** - * @var string - */ - private $inputGroupPrepend; - - /** - * @var string - */ - private $label = ''; - - /** - * @var bool - */ - private $isLabelHidden = false; - - /** - * @var bool - */ - private $isMultiple = false; - - /** - * @var string - */ - private $maxDateTime; - - /** - * @var string - */ - private $minDateTime; - - /** - * @var string - */ - private $name = ''; - - /** - * @var string - */ - private $operator = ''; - /** * @var Model */ private $parent = null; /** - * string. - */ - private $placeholder = null; - - /** - * @var bool - */ - private $submitOnChange = false; - - /** - * @var bool - */ - private $submitOnInput = false; - - /** - * @var string - */ - private $title; - - /** - * @var int - */ - private $threshold = 0; - - /** - * @var string|array|int|\DateTime|\Date + * @var string|array|int|DateTimeInterface */ private $value; /** - * @var string + * @var string|int */ private $valueType; /** - * @var string - */ - private $widget; - - public function getName(): string - { - return $this->name; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - /** - * @return array|string|int + * @return array|string|int|DateTimeInterface */ public function getValue() { - if (empty($this->value)) { - return $this->getDefaultValue(); - } - return $this->value; } /** - * @param string|array|int $value + * @param string|array|int|DateTimeInterface $value */ public function setValue($value): void { $this->value = $value; } - public function getDefaultValue(): string - { - return $this->defaultValue; - } - - public function setDefaultValue(string $defaultValue): void - { - $this->defaultValue = $defaultValue; - } - - public function getContext(): self - { - return $this; - } - - public function getIterator(): \ArrayIterator - { - return new \ArrayIterator($this); - } - - public function isInitial(): bool - { - return $this->initial; - } - - public function setInitial(): void - { - $this->initial = true; - } - /** * @return Model */ @@ -236,9 +64,6 @@ public function getParent(): ?Model return $this->parent; } - /** - * @param Model $parent - */ public function setParent(?Model $parent): void { $this->parent = $parent; @@ -254,258 +79,28 @@ public function setFormBuilder(FormBuilderInterface $formBuilder): void $this->formBuilder = $formBuilder; } - public function getField(): string - { - return $this->field; - } - - public function setField(string $field): void + public function getElementConfig(): FilterConfigElementModel { - $this->field = $field; + return $this->elementConfig; } - public function getOperator(): string + public function setElementConfig(FilterConfigElementModel $elementConfig): void { - return $this->operator; - } - - public function setOperator(string $operator): void - { - $this->operator = $operator; - } - - public function getId(): int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; + $this->elementConfig = $elementConfig; } /** - * @return null + * @return int|string */ - public function getPlaceholder() + public function getValueType() { - return $this->placeholder; + return $this->valueType; } /** - * @param null $placeholder + * @param int|string $valueType */ - public function setPlaceholder($placeholder): void - { - $this->placeholder = $placeholder; - } - - public function getTitle(): string - { - return $this->title; - } - - public function setTitle(string $title): void - { - $this->title = $title; - } - - public function getLabel(): string - { - return $this->label; - } - - public function setLabel(string $label): void - { - $this->label = $label; - } - - public function isLabelHidden(): bool - { - return $this->isLabelHidden; - } - - public function hideLabel(): void - { - $this->isLabelHidden = true; - } - - public function isSubmitOnChange(): bool - { - return $this->submitOnChange; - } - - public function setSubmitOnChange(bool $submitOnChange): void - { - $this->submitOnChange = $submitOnChange; - } - - public function isMultiple(): bool - { - return $this->isMultiple; - } - - public function setMultiple(bool $isMultiple): void - { - $this->isMultiple = $isMultiple; - } - - public function isExpanded(): bool - { - return $this->expanded; - } - - public function setExpanded(bool $expanded): void - { - $this->expanded = $expanded; - } - - public function getDateTimeFormat(): string - { - return $this->dateTimeFormat; - } - - public function setDateTimeFormat(string $dateTimeFormat): void - { - $this->dateTimeFormat = $dateTimeFormat; - } - - public function getMaxDateTime(): string - { - return $this->maxDateTime; - } - - public function setMaxDateTime(string $maxDateTime): void - { - $this->maxDateTime = $maxDateTime; - } - - public function getMinDateTime(): string - { - return $this->minDateTime; - } - - public function setMinDateTime(string $minDateTime): void - { - $this->minDateTime = $minDateTime; - } - - public function getCssClass(): string - { - return $this->cssClass; - } - - public function setCssClass(string $class): void - { - $this->cssClass = $class; - } - - public function getButtonType(): string - { - return $this->buttonType; - } - - public function setButtonType(string $buttonType): void - { - $this->buttonType = $buttonType; - } - - public function isCustomLabel(): bool - { - return $this->customLabel; - } - - public function setCustomLabel(bool $customLabel): void - { - $this->customLabel = $customLabel; - } - - public function isHtml5(): bool - { - return $this->html5; - } - - public function setHtml5(bool $html5): void - { - $this->html5 = $html5; - } - - public function getWidget(): string - { - return $this->widget; - } - - public function setWidget(string $widget): void - { - $this->widget = $widget; - } - - public function getThreshold(): int - { - return $this->threshold; - } - - public function setThreshold(int $threshold): void - { - $this->threshold = $threshold; - } - - public function getDebounce(): int - { - return $this->debounce; - } - - public function setDebounce(int $debounce): void - { - $this->debounce = $debounce; - } - - public function isSubmitOnInput(): bool - { - return $this->submitOnInput; - } - - public function setSubmitOnInput(bool $submitOnInput): void - { - $this->submitOnInput = $submitOnInput; - } - - public function hasInputGroup(): bool - { - return $this->addInputGroup; - } - - public function setInputGroup(bool $addInputGroup): void - { - $this->addInputGroup = $addInputGroup; - } - - public function getInputGroupAppend(): string - { - return $this->inputGroupAppend; - } - - public function setInputGroupAppend(string $inputGroupAppend): void - { - $this->inputGroupAppend = $inputGroupAppend; - } - - public function getInputGroupPrepend(): string - { - return $this->inputGroupPrepend; - } - - public function setInputGroupPrepend(string $inputGroupPrepend): void - { - $this->inputGroupPrepend = $inputGroupPrepend; - } - - public function getValueType(): string - { - return $this->valueType; - } - - public function setValueType(string $valueType): void + public function setValueType($valueType): void { $this->valueType = $valueType; } diff --git a/src/FilterType/FilterTypeInterface.php b/src/FilterType/FilterTypeInterface.php index aa9687ea..1397d0b3 100644 --- a/src/FilterType/FilterTypeInterface.php +++ b/src/FilterType/FilterTypeInterface.php @@ -15,4 +15,6 @@ public function buildQuery(FilterTypeContext $filterTypeContext); public function buildForm(FilterTypeContext $filterTypeContext); public function getPalette(string $prependPalette, string $appendPalette): string; + + public function getOperators(): array; } diff --git a/src/FilterType/Type/ButtonType.php b/src/FilterType/Type/ButtonType.php index 9e20c3d9..66677023 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/FilterType/Type/ButtonType.php @@ -41,8 +41,9 @@ public function buildQuery(FilterTypeContext $filterTypeContext): string public function buildForm(FilterTypeContext $filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); + $elementConfig = $filterTypeContext->getElementConfig(); - switch ($filterTypeContext->getButtonType()) { + switch ($elementConfig->buttonType) { case static::BUTTON_TYPE_RESET: case static::BUTTON_TYPE_SUBMIT: $symfonyButton = SymfonySubmitType::class; @@ -55,7 +56,7 @@ public function buildForm(FilterTypeContext $filterTypeContext) break; } - $builder->add($filterTypeContext->getName(), $symfonyButton, $this->getOptions($filterTypeContext)); + $builder->add($elementConfig->getElementName(), $symfonyButton, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string diff --git a/src/FilterType/Type/ChoiceType.php b/src/FilterType/Type/ChoiceType.php index caa12b80..47de22fd 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/FilterType/Type/ChoiceType.php @@ -22,9 +22,19 @@ class ChoiceType extends AbstractFilterType { const TYPE = 'choice_type'; - protected FieldOptionsChoice $fieldOptionsChoice; - protected ModelUtil $modelUtil; - protected Connection $connection; + + /** + * @var FieldOptionsChoice + */ + protected $fieldOptionsChoice; + /** + * @var ModelUtil + */ + protected $modelUtil; + /** + * @var Connection + */ + protected $connection; public function __construct( FilterQueryPartProcessor $filterQueryPartProcessor, @@ -48,7 +58,7 @@ public static function getType(): string public function buildForm($filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - $builder->add($filterTypeContext->getName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); + $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string @@ -75,12 +85,13 @@ public function getOperators(): array public function getOptions(FilterTypeContext $filterTypeContext): array { + $elementConfig = $filterTypeContext->getElementConfig(); $options = parent::getOptions($filterTypeContext); $options['choices'] = array_flip($this->collectChoices($filterTypeContext)); $options['choice_translation_domain'] = false; - $options['expanded'] = $filterTypeContext->isExpanded(); + $options['expanded'] = $elementConfig->expanded; - if ($filterTypeContext->isSubmitOnChange()) { + if ((bool) $elementConfig->submitOnChange) { if ($filterTypeContext->getParent()->asyncFormSubmit) { $options['attr']['data-submit-on-change'] = 1; } else { @@ -100,15 +111,15 @@ public function getOptions(FilterTypeContext $filterTypeContext): array unset($options['attr']['placeholder']); $options['required'] = false; - $options['empty_data'] = true === $filterTypeContext->isMultiple() ? [] : ''; + $options['empty_data'] = true === $elementConfig->multiple ? [] : ''; } - $options['multiple'] = $filterTypeContext->isMultiple(); + $options['multiple'] = $elementConfig->multiple; $options['data'] = $filterTypeContext->getValue(); // forgiving array handling - if (true === $filterTypeContext->isMultiple() && isset($options['data'])) { + if ((bool) $elementConfig->multiple && isset($options['data'])) { $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; } @@ -118,15 +129,15 @@ public function getOptions(FilterTypeContext $filterTypeContext): array /** * Get the list of available choices. */ - public function collectChoices(FilterTypeContext $context): array + public function collectChoices(FilterTypeContext $filterTypeContext): array { - if (null === ($element = $this->modelUtil->findModelInstanceByPk('tl_filter_config_element', $context->getId()))) { + if (null === $filterTypeContext->getElementConfig()) { return []; } return $this->fieldOptionsChoice->getCachedChoices([ - 'element' => $element, - 'filter' => $context->getParent()->row(), + 'element' => $filterTypeContext->getElementConfig(), + 'filter' => $filterTypeContext->getParent()->row(), ]); } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 5fdcfe51..39a97843 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -58,7 +58,7 @@ public function buildQuery(FilterTypeContext $filterTypeContext) public function buildForm($filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - $builder->add($filterTypeContext->getName(), SymfonyDateTimeType::class, $this->getOptions($filterTypeContext)); + $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyDateTimeType::class, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string @@ -68,7 +68,7 @@ public function getPalette(string $prependPalette, string $appendPalette): strin public function getInitialPalette(string $prependPalette, string $appendPalette) { - return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,dateTimeFormat,defaultValue;'.$appendPalette; } public function getOperators(): array @@ -92,23 +92,24 @@ public function getOperators(): array public function getOptions(FilterTypeContext $filterTypeContext): array { + $elementConfig = $filterTypeContext->getElementConfig(); $options = parent::getOptions($filterTypeContext); - $format = $filterTypeContext->getDateTimeFormat() ?: 'd.m.Y H:i'; + $format = $elementConfig->dateTimeFormat ?: 'd.m.Y H:i'; - $options['widget'] = $filterTypeContext->getWidget(); + $options['widget'] = $elementConfig->dateWidget; - switch ($filterTypeContext->getWidget()) { + switch ($elementConfig->dateWidget) { case static::WIDGET_TYPE_SINGLE_TEXT: - if ($filterTypeContext->isHtml5()) { - $options['html5'] = $filterTypeContext->isHtml5(); + if ($elementConfig->html5) { + $options['html5'] = $elementConfig->html5; $options['date_format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); - if ($filterTypeContext->getMinDateTime()) { - $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + if ('' !== $elementConfig->minDateTime) { + $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used } - if ($filterTypeContext->getMaxDateTime()) { - $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); // valid rfc 3339 date `YYYY-MM-DD` format must be used + if ('' !== $elementConfig->maxDateTime) { + $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used } } else { $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); @@ -119,12 +120,12 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['attr']['data-enable-time'] = 'true'; $options['attr']['data-date-format'] = $format; - if ($filterTypeContext->getMinDateTime()) { - $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); + if ('' !== $elementConfig->minDateTime) { + $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); } - if ($filterTypeContext->getMaxDateTime()) { - $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); + if ('' !== $elementConfig->maxDateTime) { + $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); } break; @@ -137,12 +138,12 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $minYear = Date::parse('Y', strtotime('-5 year', $time)); $maxYear = Date::parse('Y', strtotime('+5 year', $time)); - if ($filterTypeContext->getMinDateTime()) { - $minYear = Date::parse('Y', $this->dateUtil->getTimeStamp($filterTypeContext->getMinDateTime())); + if ('' !== $elementConfig->minDateTime) { + $minYear = Date::parse('Y', $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); } - if ($filterTypeContext->getMaxDateTime()) { - $maxYear = Date::parse('Y', $this->dateUtil->getTimeStamp($filterTypeContext->getMaxDateTime())); + if ('' !== $elementConfig->maxDateTime) { + $maxYear = Date::parse('Y', $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); } $options['years'] = range($minYear, $maxYear, 1); @@ -154,7 +155,7 @@ public function getOptions(FilterTypeContext $filterTypeContext): array if (empty($filterTypeContext->getValue())) { $options['data'] = null; } else { - $options['data'] = date_create_from_format($filterTypeContext->getDateTimeFormat(), $filterTypeContext->getValue()); + $options['data'] = date_create_from_format($format, $filterTypeContext->getValue()); } return $options; diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index c96464a8..9cbee132 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -11,7 +11,6 @@ use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; -use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\Extension\Core\Type\TextType as SymfonyTextType; class TextType extends AbstractFilterType implements InitialFilterTypeInterface @@ -32,8 +31,7 @@ public function buildQuery(FilterTypeContext $filterTypeContext) public function buildForm(FilterTypeContext $filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); - - $builder->add($filterTypeContext->getName(), SymfonyTextType::class, $this->getOptions($filterTypeContext)); + $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyTextType::class, $this->getOptions($filterTypeContext)); } public function getPalette(string $prependPalette, string $appendPalette): string @@ -46,33 +44,16 @@ public function getInitialPalette(string $prependPalette, string $appendPalette) return $prependPalette.'{config_legend},field,operator;'.$appendPalette; } - public function getOperators(): array - { - //remove this operators from the DatabaseUtil::OPERATORS array - $remove = [ - DatabaseUtil::OPERATOR_GREATER, - DatabaseUtil::OPERATOR_GREATER_EQUAL, - DatabaseUtil::OPERATOR_LOWER, - DatabaseUtil::OPERATOR_LOWER_EQUAL, - DatabaseUtil::OPERATOR_IN, - DatabaseUtil::OPERATOR_NOT_IN, - DatabaseUtil::OPERATOR_IS_NULL, - DatabaseUtil::OPERATOR_IS_NOT_NULL, - DatabaseUtil::OPERATOR_IS_EMPTY, - DatabaseUtil::OPERATOR_IS_NOT_EMPTY, - ]; - - return array_values(array_diff(parent::getOperators(), $remove)); - } - public function getOptions(FilterTypeContext $filterTypeContext): array { $options = parent::getOptions($filterTypeContext); - if ($filterTypeContext->isSubmitOnInput() && (bool) $filterTypeContext->getParent()->row()['asyncFormSubmit']) { + $elementConfig = $filterTypeContext->getElementConfig(); + + if ((bool) $elementConfig->submitOnInput && (bool) $filterTypeContext->getParent()->row()['asyncFormSubmit']) { $options['attr']['data-submit-on-input'] = '1'; - $options['attr']['data-threshold'] = $filterTypeContext->getThreshold(); - $options['attr']['data-debounce'] = $filterTypeContext->getDebounce(); + $options['attr']['data-threshold'] = $elementConfig->threshold ?: '0'; + $options['attr']['data-debounce'] = $elementConfig->debounce ?: '0'; } return $options; diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 2477f2c5..475c4717 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -13,6 +13,7 @@ use Contao\System; use HeimrichHannot\FilterBundle\Config\FilterConfig; use HeimrichHannot\FilterBundle\Exception\MissingFilterConfigException; +use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; @@ -116,9 +117,9 @@ protected function buildElements(FormBuilderInterface $builder, array $options) } $wrappers = []; - $types = \System::getContainer()->get('huh.filter.choice.type')->getCachedChoices(); + $types = System::getContainer()->get('huh.filter.choice.type')->getCachedChoices(); - $newTypes = \System::getContainer()->get('huh.filter.filter_type.collection')->getTypes(); + $newTypes = System::getContainer()->get(FilterTypeCollection::class)->getTypes(); $types = array_merge($types, $newTypes); if (!\is_array($types) || empty($types)) { @@ -188,48 +189,14 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil } $request = Request::createFromGlobals(); - $context = new FilterTypeContext(); if (null !== $request->query->get($element->getRelated('pid')->name)[$element->getElementName()]) { $context->setValue($request->query->get($element->getRelated('pid')->name)[$element->getElementName()]); } - - $context->setId($element->id); - $context->setName($element->getElementName()); - $context->setDefaultValue($element->addDefaultValue ? $element->defaultValue : ''); - $context->setPlaceholder($element->placeholder); + $context->setElementConfig($element); $context->setFormBuilder($builder); - $context->setTitle($element->title); - $context->setLabel($element->label); $context->setParent($element->getRelated('pid')); - $context->setSubmitOnChange($element->submitOnChange); - $context->setExpanded($element->expanded); - $context->setMultiple($element->multiple); - $context->setDateTimeFormat($element->dateTimeFormat); - $context->setMinDateTime($element->minDateTime); - $context->setMaxDateTime($element->maxDateTime); - $context->setCssClass($element->cssClass); - $context->setButtonType($element->buttonType); - $context->setCustomLabel($element->customLabel); - $context->setHtml5($element->html5); - $context->setWidget($element->dateWidget); - - if ($element->submitOnInput) { - $context->setSubmitOnInput($element->submitOnInput); - $context->setThreshold($element->threshold); - $context->setDebounce($element->debounce); - } - - if ((bool) $element->inputGroup) { - $context->setInputGroup(true); - $context->setInputGroupAppend($element->inputGroupAppend); - $context->setInputGroupPrepend($element->inputGroupPrepend); - } - - if ($element->hideLabel) { - $context->hideLabel(); - } try { $filterType->buildForm($context); diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index d6fe020c..f6506343 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,6 +1,6 @@ huh: filter: - disable_legacy_filters: true +# disable_legacy_filters: true types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } diff --git a/src/Resources/config/filter.yml b/src/Resources/config/filter.yml deleted file mode 100644 index 2709c42d..00000000 --- a/src/Resources/config/filter.yml +++ /dev/null @@ -1,4 +0,0 @@ -doctrine: - orm: - filters: - text_type: HeimrichHannot\FilterBundle\Filter\Filter \ No newline at end of file diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 9ac6ba50..fff7acfa 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -185,10 +185,6 @@ services: arguments: - !tagged huh.filter.filter_type - huh.filter.filter_type.collection: '@HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection' + HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection: ~ - HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection: - huh.filter.filter_query_part_collection: '@HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection' - - HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor: - huh.filter.filter_query_part_processor: '@HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor' + HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor: ~ From 5ca0331ef5d5ad672def0492541a1eca71814219 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 23 Apr 2021 16:20:01 +0200 Subject: [PATCH 28/58] added deprecations --- src/Config/FilterConfig.php | 3 ++- src/DataContainer/FilterConfigElementContainer.php | 9 ++++++--- src/Filter/Type/ButtonType.php | 3 +++ src/Filter/Type/ChoiceType.php | 3 +++ src/Filter/Type/DateTimeType.php | 3 +++ src/Filter/Type/ResetType.php | 3 +++ src/Filter/Type/SubmitType.php | 3 +++ src/Filter/Type/TextType.php | 3 +++ src/FilterType/PlaceholderFilterTypeInterface.php | 14 ++++++++++++++ src/FilterType/Type/TextType.php | 8 +++++++- .../languages/de/tl_filter_config_element.php | 12 ++++++------ 11 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 src/FilterType/PlaceholderFilterTypeInterface.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 7a6288b8..60cc3aba 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -15,6 +15,7 @@ use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPart; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; @@ -301,7 +302,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip /** @noinspection PhpParamsInspection */ $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection, $this->getFilter())); - /* + /** * @var FilterQueryPart */ foreach ($event->getPartsCollection()->getParts() as $part) { diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 76302e98..1a7c7136 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -12,6 +12,7 @@ use HeimrichHannot\FilterBundle\Choice\TypeChoice; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\FilterType\PlaceholderFilterTypeInterface; use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\UtilsBundle\Choice\MessageChoice; @@ -99,11 +100,13 @@ public function onOperatorOptionsCallback(DataContainer $dc) public function onPlaceholderOptionsCallback(DataContainer $dc): array { - if (!$this->bundleConfig['filter']['disable_legacy_filers']) { - return $this->messageChoice->getCachedChoices('huh.filter.placeholder'); + $placeholders = $this->messageChoice->getCachedChoices('huh.filter.placeholder'); + + if ($dc->activeRecord->type instanceof PlaceholderFilterTypeInterface) { + return array_merge($placeholders, $this->typeCollection->getType($dc->activeRecord->type)->getPlaceholders()); } - return $this->typeCollection->getType($dc->activeRecord->type)->getPlaceholders(); + return $placeholders; } public function onDateWidgetOptionsCallback(DataContainer $dc): array diff --git a/src/Filter/Type/ButtonType.php b/src/Filter/Type/ButtonType.php index 61580ee5..21e313eb 100644 --- a/src/Filter/Type/ButtonType.php +++ b/src/Filter/Type/ButtonType.php @@ -13,6 +13,9 @@ use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + */ class ButtonType extends AbstractType { const TYPE = 'button'; diff --git a/src/Filter/Type/ChoiceType.php b/src/Filter/Type/ChoiceType.php index 94456235..98307917 100644 --- a/src/Filter/Type/ChoiceType.php +++ b/src/Filter/Type/ChoiceType.php @@ -16,6 +16,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ChoiceType + */ class ChoiceType extends AbstractType { const TYPE = 'choice'; diff --git a/src/Filter/Type/DateTimeType.php b/src/Filter/Type/DateTimeType.php index bcce1644..80e9d9e7 100644 --- a/src/Filter/Type/DateTimeType.php +++ b/src/Filter/Type/DateTimeType.php @@ -17,6 +17,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\DateTimeType + */ class DateTimeType extends AbstractType { const TYPE = 'date_time'; diff --git a/src/Filter/Type/ResetType.php b/src/Filter/Type/ResetType.php index 6d0cdf2e..52ac1260 100644 --- a/src/Filter/Type/ResetType.php +++ b/src/Filter/Type/ResetType.php @@ -13,6 +13,9 @@ use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + */ class ResetType extends AbstractType { /** diff --git a/src/Filter/Type/SubmitType.php b/src/Filter/Type/SubmitType.php index e9456fce..9dc39552 100644 --- a/src/Filter/Type/SubmitType.php +++ b/src/Filter/Type/SubmitType.php @@ -13,6 +13,9 @@ use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + */ class SubmitType extends AbstractType { const TYPE = 'submit'; diff --git a/src/Filter/Type/TextType.php b/src/Filter/Type/TextType.php index c97a5e21..20d53c2b 100644 --- a/src/Filter/Type/TextType.php +++ b/src/Filter/Type/TextType.php @@ -14,6 +14,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\TextType + */ class TextType extends AbstractType { const TYPE = 'text'; diff --git a/src/FilterType/PlaceholderFilterTypeInterface.php b/src/FilterType/PlaceholderFilterTypeInterface.php new file mode 100644 index 00000000..6fd1b987 --- /dev/null +++ b/src/FilterType/PlaceholderFilterTypeInterface.php @@ -0,0 +1,14 @@ + [ 'miscellaneous' => 'Sonstiges', - 'text' => 'Text', + 'text' => 'Text - [deprecated]', 'text_concat' => 'Konkatenierter Text', 'textarea' => 'Textarea', \HeimrichHannot\FilterBundle\Filter\Type\EmailType::TYPE => 'E-Mail', @@ -166,7 +166,7 @@ \HeimrichHannot\FilterBundle\Filter\Type\MultipleRangeType::TYPE => 'Multi-Feld-Spanne (range)', 'tel' => 'Telefon', 'color' => 'Farbe', - 'choice' => 'Choice', + 'choice' => 'Choice - [deprecated]', \HeimrichHannot\FilterBundle\Filter\Type\RadiusChoiceType::TYPE => 'Radius-Choice', 'country' => 'Land', \HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::TYPE => 'Umkreissuche', @@ -175,15 +175,15 @@ 'parent' => 'Elternentität', 'skip_parents' => 'Elternentitäten ausschließen', 'visible' => 'Veröffentlicht', - 'button' => 'Button', - 'reset' => 'Reset', - 'submit' => 'Submit', + 'button' => 'Button - [deprecated]', + 'reset' => 'Reset - [deprecated]', + 'submit' => 'Submit - [deprecated]', 'hidden' => 'Hidden', 'checkbox' => 'Checkbox', 'radio' => 'Radio', 'other' => 'Sonstiges', 'initial' => 'Initial', - \HeimrichHannot\FilterBundle\Filter\Type\DateTimeType::TYPE => 'Datum & Zeit', + \HeimrichHannot\FilterBundle\Filter\Type\DateTimeType::TYPE => 'Datum & Zeit - [deprecated]', \HeimrichHannot\FilterBundle\Filter\Type\DateType::TYPE => 'Datum', 'time' => 'Zeit', \HeimrichHannot\FilterBundle\Filter\Type\DateRangeType::TYPE => 'Datumsspanne (date range)', From ca4cc6b8552a30de0e02ad56911d25dca0271b94 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 23 Apr 2021 17:24:20 +0200 Subject: [PATCH 29/58] added initialValue and initialValueType --- .../FilterConfigElementContainer.php | 26 ++++++++++++++++++- src/Filter/AbstractType.php | 1 - src/FilterType/AbstractFilterType.php | 11 ++++++++ src/FilterType/InitialFilterTypeInterface.php | 2 ++ src/FilterType/Type/DateTimeType.php | 5 ++++ src/FilterType/Type/TextType.php | 11 +++++++- .../contao/dca/tl_filter_config_element.php | 2 +- 7 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 1a7c7136..3d4f29a5 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -9,7 +9,10 @@ namespace HeimrichHannot\FilterBundle\DataContainer; use Contao\DataContainer; +use Contao\DC_Table; +use Contao\System; use HeimrichHannot\FilterBundle\Choice\TypeChoice; +use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; use HeimrichHannot\FilterBundle\FilterType\PlaceholderFilterTypeInterface; @@ -72,7 +75,7 @@ public function onLoadCallback(DataContainer $dc): void } } - public function onTypeOptionsCallback(DataContainer $dc) + public function onTypeOptionsCallback(DataContainer $dc): array { $options = $this->typeChoice->getCachedChoices($dc); @@ -89,6 +92,27 @@ public function onTypeOptionsCallback(DataContainer $dc) return $options; } + public function onInitialValueTypeCallback(DC_Table $dc): array + { + $choices = AbstractFilterType::VALUE_TYPES; + $activeRecord = $dc->activeRecord->fetchAllAssoc()[0]; + + if (empty($activeRecord)) { + return $choices; + } + + $types = System::getContainer()->getParameter('huh.filter')['filter']['types']; + $typeIndex = array_search($activeRecord['type'], array_column($types, 'name'), true); + + if (!$typeIndex && $this->typeCollection->getType($activeRecord['type']) instanceof InitialFilterTypeInterface) { + return $this->typeCollection->getType($activeRecord['type'])->getInitialValueTypes($choices); + } + + $class = $types[$typeIndex]['class']; + + return $class::VALUE_TYPES; + } + public function onOperatorOptionsCallback(DataContainer $dc) { if (!$this->bundleConfig['filter']['disable_legacy_filters']) { diff --git a/src/Filter/AbstractType.php b/src/Filter/AbstractType.php index 21907aae..4cbd61d7 100644 --- a/src/Filter/AbstractType.php +++ b/src/Filter/AbstractType.php @@ -15,7 +15,6 @@ use HeimrichHannot\FilterBundle\Event\AdjustFilterOptionsEvent; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; -use Nelmio\SecurityBundle\ContentSecurityPolicy\Violation\Filter\Filter; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Translation\TranslatorInterface; diff --git a/src/FilterType/AbstractFilterType.php b/src/FilterType/AbstractFilterType.php index 4aae6057..90e663c5 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/FilterType/AbstractFilterType.php @@ -17,6 +17,17 @@ abstract class AbstractFilterType implements FilterTypeInterface { const GROUP_DEFAULT = 'miscellaneous'; + const VALUE_TYPE_SCALAR = 'scalar'; + const VALUE_TYPE_ARRAY = 'array'; + const VALUE_TYPE_CONTEXTUAL = 'contextual'; + const VALUE_TYPE_LATEST = 'latest'; + + const VALUE_TYPES = [ + self::VALUE_TYPE_SCALAR, + self::VALUE_TYPE_ARRAY, + self::VALUE_TYPE_CONTEXTUAL, + ]; + /** * @var FilterQueryPartProcessor */ diff --git a/src/FilterType/InitialFilterTypeInterface.php b/src/FilterType/InitialFilterTypeInterface.php index a9e8fac5..631665f2 100644 --- a/src/FilterType/InitialFilterTypeInterface.php +++ b/src/FilterType/InitialFilterTypeInterface.php @@ -11,4 +11,6 @@ interface InitialFilterTypeInterface { public function getInitialPalette(string $prependPalette, string $appendPalette); + + public function getInitialValueTypes(array $types): array; } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 39a97843..14ec2395 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -71,6 +71,11 @@ public function getInitialPalette(string $prependPalette, string $appendPalette) return $prependPalette.'{config_legend},field,operator,dateTimeFormat,defaultValue;'.$appendPalette; } + public function getInitialValueTypes(array $types): array + { + return $types; + } + public function getOperators(): array { //remove this operators from the DatabaseUtil::OPERATORS array diff --git a/src/FilterType/Type/TextType.php b/src/FilterType/Type/TextType.php index 29d436e3..96341edb 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/FilterType/Type/TextType.php @@ -42,7 +42,16 @@ public function getPalette(string $prependPalette, string $appendPalette): strin public function getInitialPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,initialValueType;'.$appendPalette; + } + + public function getInitialValueTypes(array $types): array + { + $remove = [ + AbstractFilterType::VALUE_TYPE_ARRAY, + ]; + + return array_values(array_diff($types, $remove)); } public function getOptions(FilterTypeContext $filterTypeContext): array diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 4b02c136..2f3ad0ef 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -763,7 +763,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => ['huh.filter.listener.dca.callback.filterconfigelement', 'getValueTypeOptions'], + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onInitialValueTypeCallback'], 'reference' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['reference'], 'eval' => ['tl_class' => 'w50 clr', 'mandatory' => true, 'includeBlankOption' => true, 'submitOnChange' => true], 'sql' => "varchar(16) NOT NULL default ''", From 6c5d43912bc719e3cd4e16f6fa18a74951026386 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 27 Apr 2021 12:30:04 +0200 Subject: [PATCH 30/58] added intial filter handling, added new filter option for overriding initial filters --- src/Config/FilterConfig.php | 96 +++++++++++++++++-- .../FilterConfigElementContainer.php | 2 +- src/FilterQuery/FilterQueryPart.php | 96 ++++++++++++++++++- src/FilterQuery/FilterQueryPartCollection.php | 35 +++++++ src/FilterQuery/FilterQueryPartProcessor.php | 53 ++++++++-- src/FilterType/Type/DateTimeType.php | 10 +- src/Model/FilterConfigElementModel.php | 1 + .../contao/dca/tl_filter_config_element.php | 7 ++ 8 files changed, 277 insertions(+), 23 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 60cc3aba..99d1d3e8 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -29,6 +29,7 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigModel; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use HeimrichHannot\FilterBundle\Session\FilterSession; +use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Exception\TransformationFailedException; @@ -39,6 +40,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\PropertyAccess\PropertyAccess; +use System; class FilterConfig implements \JsonSerializable { @@ -253,10 +255,8 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip return; } - $types = $this->container->get('huh.filter.choice.type')->getCachedChoices(); - - $newTypes = \System::getContainer()->get(FilterTypeCollection::class)->getTypes(); - $types = array_merge($types, $newTypes); + $types = array_merge($this->container->get('huh.filter.choice.type')->getCachedChoices(), + System::getContainer()->get(FilterTypeCollection::class)->getTypes()); if (!\is_array($types) || empty($types)) { return; @@ -279,6 +279,18 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip continue; } + if (!class_exists($types[$element->type]['class'])) { + continue; + } + + $filterClass = new $types[$element->type]['class']($this); + + if (\is_array($types[$element->type]) && $filterClass instanceof AbstractType) { + $this->processOriginFilterType($element, $types[$element->type]); + + continue; + } + $config = $types[$element->type]; $class = $config['class']; $skip = $queryBuilder->getSkip(); @@ -302,16 +314,64 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip /** @noinspection PhpParamsInspection */ $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection, $this->getFilter())); + /* prepare FilterQueryParts to behave like in the original implementation + * (initial filters are overwritten by other filters for the same field, if they are after the initial filter) + * TODO: this needs to be refactored after removing legacy filters + */ + $filterTargetFields = $this->filterQueryPartCollection->getTargetFields(); + + foreach ($filterTargetFields as $targetField) { + if (1 >= \count($targetField)) { + continue; + } + + $initialFilters = array_combine(array_keys($targetField), array_column($targetField, 'initial')); + + foreach ($initialFilters as $key => $filterElement) { + if (!$targetField[$key]['initial']) { + continue; + } + //check if this filter is overridable + if (!$targetField[$key]['overridable']) { + continue; + } + + //check if there are other not initial filters for this field + $targetKey = array_search($key, array_keys($targetField), true); + + if (false !== $targetKey) { + $leftover = \array_slice($targetField, $targetKey + 1, null, true); + + if (!empty($leftover)) { + foreach ($leftover as $leftoverElement) { + if (!$leftoverElement['initial'] && null !== ($element = $this->filterQueryPartCollection->getPartByName($key))) { + $element->setDisabled(true); + } + } + } + } + } + } + /** * @var FilterQueryPart */ foreach ($event->getPartsCollection()->getParts() as $part) { + if ($part->isDisabled()) { + $this->filterQueryPartCollection->removePartByName($part->getName()); + + continue; + } + $this->queryBuilder->andWhere($this->filterQueryPartProcessor->composeWhereForQueryBuilder($part, $this->queryBuilder)); + $this->queryBuilder->setParameter( $part->getWildcard(), $part->getValue(), $part->getValueType() ); + + $this->filterQueryPartCollection->removePartByName($part->getName()); } } @@ -675,7 +735,7 @@ public function jsonSerialize() protected function processFilterType(FilterConfigElementModel $config, FilterTypeInterface $filterType) { - if (!$this->getData()[$config->getElementName()]) { + if (!$this->getData()[$config->getElementName()] && !(bool) $config->isInitial) { return; } @@ -687,6 +747,26 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $filterType->buildQuery($context); } + protected function processOriginFilterType(FilterConfigElementModel $config, array $element) + { + if (!$this->getData()[$config->field]) { + return; + } + + if ('' === $config->operator && '' !== $config->customOperator) { + $config->operator = $config->customOperator; + } elseif ('' === $config->operator && '' === $config->customOperator) { + $config->operator = DatabaseUtil::OPERATOR_EQUAL; + } + + $context = new FilterTypeContext(); + $context->setValue($this->getData()[$config->field]); + $context->setElementConfig($config); + $context->setParent($config->getRelated('pid')); + + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($context)); + } + protected function isResetButtonClicked(FormInterface $form): bool { if (!(null !== $form->getClickedButton() && \in_array($form->getClickedButton()->getName(), @@ -718,20 +798,20 @@ protected function mapFormsToData() } catch (TransformationFailedException $e) { $this->resetData(); $this->builder->setData($this->getData()); - - return; $forms = $this->builder->getForm(); } $propertyAccessor = PropertyAccess::createPropertyAccessor(); - /* + /** * @var FormInterface */ foreach ($forms as $form) { $propertyPath = $form->getPropertyPath(); $config = $form->getConfig(); + $singleData = $form->getData(); + // Write-back is disabled if the form is not synchronized (transformation failed), // if the form was not submitted and if the form is disabled (modification not allowed) if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) { diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 3d4f29a5..f3110ff8 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -67,7 +67,7 @@ public function onLoadCallback(DataContainer $dc): void if ($type instanceof InitialFilterTypeInterface && $model->isInitial) { $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; - $prependPalette = '{initial_legend},isInitial;{general_legend},title,type;'; + $prependPalette = '{initial_legend},isInitial,isInitialOverridable;{general_legend},title,type;'; $appendPalette = '{publish_legend},published;'; $dca['palettes'][$model->type] = $type->getInitialPalette($prependPalette, $appendPalette); diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php index f4f284d2..04d8968a 100644 --- a/src/FilterQuery/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPart.php @@ -12,6 +12,11 @@ class FilterQueryPart { + /** + * @var bool + */ + private $disabled = false; + /** * @var string */ @@ -22,6 +27,21 @@ class FilterQueryPart */ private $filterElementId; + /** + * @var bool + */ + private $initial = false; + + /** + * @var mixed + */ + private $initialValue; + + /** + * @var string + */ + private $initialValueType; + /** * @var string */ @@ -32,6 +52,11 @@ class FilterQueryPart */ private $operator; + /** + * @var bool + */ + private $overridable = true; + /** * @var string|int|array|\DateTime */ @@ -54,10 +79,19 @@ public function __construct(FilterTypeContext $filterTypeContext) $this->name = $elementConfig->getElementName(); $this->filterElementId = $elementConfig->id; $this->operator = $elementConfig->operator; - $this->field = $elementConfig->field; - $this->value = $filterTypeContext->getValue(); - $this->valueType = $filterTypeContext->getValueType(); + $this->field = $filterTypeContext->getParent()->row()['dataContainer'].'.'.$elementConfig->field; $this->wildcard = ':'.str_replace('.', '_', $elementConfig->field).'_'.$elementConfig->id; + + if ($elementConfig->isInitial) { + $this->initial = $elementConfig->isInitial; + $this->initialValue = $elementConfig->initialValue ?: $elementConfig->initialValueArray; + $this->initialValueType = $elementConfig->initialValueType; + $this->value = $this->initialValue; + $this->overridable = $elementConfig->isInitialOverridable; + } else { + $this->value = $filterTypeContext->getValue(); + $this->valueType = $filterTypeContext->getValueType(); + } } public function getWildcard(): string @@ -141,4 +175,60 @@ public function setOperator(string $operator): void { $this->operator = $operator; } + + public function isInitial(): bool + { + return $this->initial; + } + + public function setInitial(bool $initial): void + { + $this->initial = $initial; + } + + /** + * @return mixed + */ + public function getInitialValue() + { + return $this->initialValue; + } + + /** + * @param mixed $initialValue + */ + public function setInitialValue($initialValue): void + { + $this->initialValue = $initialValue; + } + + public function getInitialValueType(): string + { + return $this->initialValueType; + } + + public function setInitialValueType(string $initialValueType): void + { + $this->initialValueType = $initialValueType; + } + + public function isDisabled(): bool + { + return $this->disabled; + } + + public function setDisabled(bool $disabled): void + { + $this->disabled = $disabled; + } + + public function isOverridable(): bool + { + return $this->overridable; + } + + public function setOverridable(bool $overridable): void + { + $this->overridable = $overridable; + } } diff --git a/src/FilterQuery/FilterQueryPartCollection.php b/src/FilterQuery/FilterQueryPartCollection.php index 26154faa..edb4a8bb 100644 --- a/src/FilterQuery/FilterQueryPartCollection.php +++ b/src/FilterQuery/FilterQueryPartCollection.php @@ -15,18 +15,53 @@ class FilterQueryPartCollection */ private $parts = []; + /** + * @var array + */ + private $targetFields = []; + public function getParts(): array { return $this->parts; } + public function getPartByName(string $name): ?FilterQueryPart + { + if (!\array_key_exists($name, $this->parts)) { + return null; + } + + return $this->parts[$name]; + } + public function addPart(FilterQueryPart $part): void { $this->parts[$part->getName()] = $part; + $this->addTargetField($part->getField(), $part->getName(), $part->isInitial(), $part->isOverridable()); } public function removePartByName(string $name): void { unset($this->parts[$name]); } + + public function addTargetField(string $field, string $partName, bool $isInitial, bool $overridable): void + { + $this->targetFields[$field][$partName] = ['initial' => $isInitial, 'overridable' => $overridable]; + } + + public function removeTargetField(string $field, string $partName): void + { + unset($this->targetFields[$field][$partName]); + } + + public function getTargetFields(): array + { + return $this->targetFields; + } + + public function reset(): void + { + $this->parts = []; + } } diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index 7426386a..a533a914 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -9,21 +9,36 @@ namespace HeimrichHannot\FilterBundle\FilterQuery; use Contao\Controller; +use Contao\StringUtil; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; +use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Date\DateUtil; class FilterQueryPartProcessor { - protected Connection $connection; - protected DateUtil $dateUtil; - - public function __construct(Connection $connection, DateUtil $dateUtil) + /** + * @var Connection + */ + protected $connection; + + /** + * @var DateUtil + */ + protected $dateUtil; + + /** + * @var DatabaseUtil + */ + protected $databaseUtil; + + public function __construct(Connection $connection, DateUtil $dateUtil, DatabaseUtil $databaseUtil) { $this->connection = $connection; $this->dateUtil = $dateUtil; + $this->databaseUtil = $databaseUtil; } public function composeQueryPart(FilterTypeContext $filterTypeContext): FilterQueryPart @@ -41,11 +56,15 @@ public function composeWhereForQueryBuilder(FilterQueryPart $filterQueryPart, Qu $value = Controller::replaceInsertTags(\is_array($value) ? implode(' ', $value) : $value, false); } + $this->updateInitialFilterProperties($filterQueryPart); + switch ($filterQueryPart->getOperator()) { case DatabaseUtil::OPERATOR_LIKE: $where = $queryBuilder->expr()->like($filterQueryPart->getField(), $filterQueryPart->getWildcard()); - if (false === strpos($value, '%')) { + if (('%' === substr($value, -1)) || ('%' === substr($value, 0, 1))) { + $filterQueryPart->setValue($value); + } else { $filterQueryPart->setValue('%'.$value.'%'); } @@ -54,7 +73,9 @@ public function composeWhereForQueryBuilder(FilterQueryPart $filterQueryPart, Qu case DatabaseUtil::OPERATOR_UNLIKE: $where = $queryBuilder->expr()->notLike($filterQueryPart->getField(), $filterQueryPart->getWildcard()); - if (false === strpos($value, '%')) { + if (('%' === substr($value, -1)) || ('%' === substr($value, 0, 1))) { + $filterQueryPart->setValue($value); + } else { $filterQueryPart->setValue('%'.$value.'%'); } @@ -183,4 +204,24 @@ function ($val) { return $where; } + + public function updateInitialFilterProperties(FilterQueryPart &$filterPart): void + { + if (null === $filterPart->getInitialValue()) { + return; + } + + switch ($filterPart->getInitialValueType()) { + case AbstractFilterType::VALUE_TYPE_SCALAR: + $filterPart->setValue($filterPart->getInitialValue()); + + break; + + case AbstractFilterType::VALUE_TYPE_ARRAY: + $filterPart->setValue(array_column(StringUtil::deserialize($filterPart->getInitialValue()), 'value')); + $filterPart->setValueType(Connection::PARAM_STR_ARRAY); + + break; + } + } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 14ec2395..453d5513 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -157,11 +157,11 @@ public function getOptions(FilterTypeContext $filterTypeContext): array } //TODO: double check if correct - if (empty($filterTypeContext->getValue())) { - $options['data'] = null; - } else { - $options['data'] = date_create_from_format($format, $filterTypeContext->getValue()); - } +// if (empty($filterTypeContext->getValue())) { +// $options['data'] = null; +// } else { +// $options['data'] = date_create_from_format($format, $filterTypeContext->getValue()); +// } return $options; } diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index 50fa6e24..d55743e7 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -105,6 +105,7 @@ * @property string $threshold * @property string $debounce * @property bool $submitOnInput + * @property bool $isInitialOverridable * * @method FilterConfigElementModel|null findById($id, array $opt = []) * @method FilterConfigElementModel|null findByPk($id, array $opt = []) diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 2f3ad0ef..04d85ea6 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -235,6 +235,13 @@ 'eval' => ['tl_class' => 'w50', 'submitOnChange' => true], 'sql' => "char(1) NOT NULL default ''", ], + 'isInitialOverridable' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['isInitialOverridable'], + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => ['tl_class' => 'w50'], + 'sql' => "char(1) NOT NULL default ''", + ], 'title' => [ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['title'], 'exclude' => true, From 254864ab23b1f6685ea8f7855beeb1f716e2f116 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 27 Apr 2021 13:58:14 +0200 Subject: [PATCH 31/58] fixed corruption of DateTimeType by passing wrong value to formBuilder --- src/Config/FilterConfig.php | 6 ++---- src/FilterType/Type/DateTimeType.php | 11 +++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 99d1d3e8..7fc4b582 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -353,7 +353,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip } } - /** + /* * @var FilterQueryPart */ foreach ($event->getPartsCollection()->getParts() as $part) { @@ -803,15 +803,13 @@ protected function mapFormsToData() $propertyAccessor = PropertyAccess::createPropertyAccessor(); - /** + /* * @var FormInterface */ foreach ($forms as $form) { $propertyPath = $form->getPropertyPath(); $config = $form->getConfig(); - $singleData = $form->getData(); - // Write-back is disabled if the form is not synchronized (transformation failed), // if the form was not submitted and if the form is disabled (modification not allowed) if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) { diff --git a/src/FilterType/Type/DateTimeType.php b/src/FilterType/Type/DateTimeType.php index 453d5513..e4826a54 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/FilterType/Type/DateTimeType.php @@ -156,12 +156,11 @@ public function getOptions(FilterTypeContext $filterTypeContext): array break; } - //TODO: double check if correct -// if (empty($filterTypeContext->getValue())) { -// $options['data'] = null; -// } else { -// $options['data'] = date_create_from_format($format, $filterTypeContext->getValue()); -// } + if ('' === (string) $filterTypeContext->getValue()) { + $options['data'] = null; + } else { + $options['data'] = date_create_from_format($format, $filterTypeContext->getValue()); + } return $options; } From a4c5831a74883b3878c6a42da6a71e6e7a629d92 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 27 Apr 2021 14:18:35 +0200 Subject: [PATCH 32/58] fixed wrong indent in service yml --- src/Resources/config/services.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 54006c80..594f1f79 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -75,7 +75,7 @@ services: huh.filter.choice.type: '@HeimrichHannot\FilterBundle\Choice\TypeChoice' HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice: ~ - huh.filter.choice.field_options: '@HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice' + huh.filter.choice.field_options: '@HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice' huh.filter.choice.country: class: HeimrichHannot\FilterBundle\Choice\CountryChoice From 20951077616498025b8f1e4d40388de666486f80 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 29 Apr 2021 10:36:01 +0200 Subject: [PATCH 33/58] refactored type and reordered new classes --- src/Config/FilterConfig.php | 136 ++++++++++-------- .../FilterConfigElementContainer.php | 10 +- src/Event/ModifyFilterQueryPartsEvent.php | 12 +- .../LoadDataContainerListener.php | 4 +- src/Filter/Type/ParentType.php | 2 +- src/FilterQuery/FilterQueryPart.php | 10 +- src/FilterQuery/FilterQueryPartProcessor.php | 6 +- src/Form/FilterType.php | 8 +- src/Manager/FilterManager.php | 2 +- src/Model/FilterConfigElementModel.php | 4 +- src/Processor/FilterContext.php | 57 ++++++++ src/Resources/config/config.yml | 2 +- src/Resources/config/services.yml | 12 +- .../languages/de/tl_filter_config_element.php | 12 +- .../languages/en/tl_filter_config_element.php | 4 +- .../AbstractFilterType.php | 2 +- .../Type => Type/Concrete}/ButtonType.php | 6 +- .../Type => Type/Concrete}/ChoiceType.php | 10 +- .../Type => Type/Concrete}/DateTimeType.php | 8 +- .../Type => Type/Concrete}/TextType.php | 12 +- .../FilterTypeCollection.php | 2 +- .../FilterTypeContext.php | 18 +-- .../FilterTypeInterface.php | 2 +- .../InitialFilterTypeInterface.php | 2 +- .../PlaceholderFilterTypeInterface.php | 2 +- tests/ContaoManager/PluginTest.php | 2 +- tests/Filter/Type/ParentTypeTest.php | 4 +- 27 files changed, 215 insertions(+), 136 deletions(-) create mode 100644 src/Processor/FilterContext.php rename src/{FilterType => Type}/AbstractFilterType.php (99%) rename src/{FilterType/Type => Type/Concrete}/ButtonType.php (90%) rename src/{FilterType/Type => Type/Concrete}/ChoiceType.php (93%) rename src/{FilterType/Type => Type/Concrete}/DateTimeType.php (96%) rename src/{FilterType/Type => Type/Concrete}/TextType.php (82%) rename src/{FilterType => Type}/FilterTypeCollection.php (96%) rename src/{FilterType => Type}/FilterTypeContext.php (80%) rename src/{FilterType => Type}/FilterTypeInterface.php (88%) rename src/{FilterType => Type}/InitialFilterTypeInterface.php (85%) rename src/{FilterType => Type}/PlaceholderFilterTypeInterface.php (78%) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 7fc4b582..54db7437 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -12,24 +12,26 @@ use Contao\CoreBundle\Framework\ContaoFrameworkInterface; use Contao\Environment; use Contao\InsertTags; +use Contao\Model; use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; -use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPart; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; -use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; use HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension; use HeimrichHannot\FilterBundle\Form\FilterType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\FilterBundle\Model\FilterConfigModel; +use HeimrichHannot\FilterBundle\Processor\FilterContext; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use HeimrichHannot\FilterBundle\Session\FilterSession; +use HeimrichHannot\FilterBundle\Type\Concrete\ButtonType; +use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\FilterTypeInterface; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\Exception\TransformationFailedException; @@ -115,6 +117,10 @@ class FilterConfig implements \JsonSerializable * @var FilterQueryPartProcessor */ protected $filterQueryPartProcessor; + /** + * @var ModelUtil + */ + protected $modelUtil; /** * @var ContainerInterface @@ -137,7 +143,8 @@ public function __construct( RequestStack $requestStack, FilterQueryPartCollection $filterQueryPartCollection, EventDispatcherInterface $eventDispatcher, - FilterQueryPartProcessor $filterQueryPartProcessor + FilterQueryPartProcessor $filterQueryPartProcessor, + ModelUtil $modelUtil ) { $this->framework = $framework; $this->session = $session; @@ -147,6 +154,7 @@ public function __construct( $this->filterQueryPartCollection = $filterQueryPartCollection; $this->eventDispatcher = $eventDispatcher; $this->filterQueryPartProcessor = $filterQueryPartProcessor; + $this->modelUtil = $modelUtil; } /** @@ -170,6 +178,12 @@ public function buildForm(array $data = []) return; } + foreach ($this->elements as $element) { + if (ButtonType::TYPE === $element->type && ButtonType::BUTTON_TYPE_RESET === $element->buttonType) { + $this->addResetName($element->getElementName()); + } + } + $factory = Forms::createFormFactoryBuilder()->addTypeExtensions([ new FormTypeExtension(), new FormButtonExtension(), @@ -210,12 +224,6 @@ public function buildForm(array $data = []) $this->builder = $factory->createNamedBuilder($this->filter['name'], FilterType::class, $data, $options); - foreach ($this->elements as $element) { - if (ButtonType::TYPE === $element->type && ButtonType::BUTTON_TYPE_RESET === $element->buttonType) { - $this->addResetName($element->getElementName()); - } - } - $this->mapFormsToData(); } @@ -286,7 +294,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $filterClass = new $types[$element->type]['class']($this); if (\is_array($types[$element->type]) && $filterClass instanceof AbstractType) { - $this->processOriginFilterType($element, $types[$element->type]); + $this->processLegacyFilterType($element, $types[$element->type]); continue; } @@ -309,54 +317,26 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $type->buildQuery($queryBuilder, $element); } + if (null === ($filterConfigModel = $this->modelUtil->findModelInstanceByPk('tl_filter_config', $this->getFilter()['id']))) { + return; + } + + $filterContext = new FilterContext($filterConfigModel, $this->getData()); + //apply parts from FilterQueryPartCollection /** @noinspection PhpMethodParametersCountMismatchInspection */ /** @noinspection PhpParamsInspection */ - $event = $this->eventDispatcher->dispatch(ModifyFilterQueryPartsEvent::NAME, new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection, $this->getFilter())); - - /* prepare FilterQueryParts to behave like in the original implementation - * (initial filters are overwritten by other filters for the same field, if they are after the initial filter) - * TODO: this needs to be refactored after removing legacy filters - */ - $filterTargetFields = $this->filterQueryPartCollection->getTargetFields(); - - foreach ($filterTargetFields as $targetField) { - if (1 >= \count($targetField)) { - continue; - } - - $initialFilters = array_combine(array_keys($targetField), array_column($targetField, 'initial')); - - foreach ($initialFilters as $key => $filterElement) { - if (!$targetField[$key]['initial']) { - continue; - } - //check if this filter is overridable - if (!$targetField[$key]['overridable']) { - continue; - } - - //check if there are other not initial filters for this field - $targetKey = array_search($key, array_keys($targetField), true); + $event = $this->eventDispatcher->dispatch( + ModifyFilterQueryPartsEvent::NAME, + new ModifyFilterQueryPartsEvent($this->filterQueryPartCollection, $filterContext) + ); - if (false !== $targetKey) { - $leftover = \array_slice($targetField, $targetKey + 1, null, true); - - if (!empty($leftover)) { - foreach ($leftover as $leftoverElement) { - if (!$leftoverElement['initial'] && null !== ($element = $this->filterQueryPartCollection->getPartByName($key))) { - $element->setDisabled(true); - } - } - } - } - } - } + $this->prepareFilterQueryParts($event->getPartsCollection()); /* * @var FilterQueryPart */ - foreach ($event->getPartsCollection()->getParts() as $part) { + foreach ($this->filterQueryPartCollection->getParts() as $part) { if ($part->isDisabled()) { $this->filterQueryPartCollection->removePartByName($part->getName()); @@ -413,6 +393,7 @@ public function handleForm($request = null): ?RedirectResponse } $data = $form->getData(); + $data['f_submitted'] = true; $url = $this->container->get('huh.utils.url')->removeQueryString([$form->getName()], $url ?: null); @@ -742,12 +723,12 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp $context = new FilterTypeContext(); $context->setValue($this->getData()[$config->getElementName()]); $context->setElementConfig($config); - $context->setParent($config->getRelated('pid')); + $context->setFilterConfig($config->getRelated('pid')); $filterType->buildQuery($context); } - protected function processOriginFilterType(FilterConfigElementModel $config, array $element) + protected function processLegacyFilterType(FilterConfigElementModel $config, array $element) { if (!$this->getData()[$config->field]) { return; @@ -756,13 +737,14 @@ protected function processOriginFilterType(FilterConfigElementModel $config, arr if ('' === $config->operator && '' !== $config->customOperator) { $config->operator = $config->customOperator; } elseif ('' === $config->operator && '' === $config->customOperator) { + //TODO: get from Class $config->operator = DatabaseUtil::OPERATOR_EQUAL; } $context = new FilterTypeContext(); $context->setValue($this->getData()[$config->field]); $context->setElementConfig($config); - $context->setParent($config->getRelated('pid')); + $context->setFilterConfig($config->getRelated('pid')); $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($context)); } @@ -830,4 +812,46 @@ protected function mapFormsToData() $this->builder->setData($data); } + + /* prepare FilterQueryParts to behave like in the original implementation + * (initial filters are overwritten by other filters for the same field, if they are after the initial filter) + * TODO: this needs to be refactored after removing legacy filters + */ + private function prepareFilterQueryParts(FilterQueryPartCollection $filterQueryPartCollection) + { + foreach ($filterQueryPartCollection->getTargetFields() as $targetField) { + if (1 >= \count($targetField)) { + continue; + } + + $initialFilters = array_combine(array_keys($targetField), array_column($targetField, 'initial')); + + foreach ($initialFilters as $key => $filterElement) { + if (!$targetField[$key]['initial']) { + continue; + } + //check if this filter is overridable + if (!$targetField[$key]['overridable']) { + continue; + } + + //check if there are other not initial filters for this field + $targetKey = array_search($key, array_keys($targetField), true); + + if (false !== $targetKey) { + $leftover = \array_slice($targetField, $targetKey + 1, null, true); + + if (!empty($leftover)) { + foreach ($leftover as $leftoverElement) { + if (!$leftoverElement['initial'] && null !== ($element = $this->filterQueryPartCollection->getPartByName($key))) { + $element->setDisabled(true); + } + } + } + } + } + } + + $this->filterQueryPartCollection = $filterQueryPartCollection; + } } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index f3110ff8..66316cd7 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -12,12 +12,12 @@ use Contao\DC_Table; use Contao\System; use HeimrichHannot\FilterBundle\Choice\TypeChoice; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; -use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; -use HeimrichHannot\FilterBundle\FilterType\PlaceholderFilterTypeInterface; -use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\Concrete\ButtonType; +use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; +use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\Type\PlaceholderFilterTypeInterface; use HeimrichHannot\UtilsBundle\Choice\MessageChoice; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; diff --git a/src/Event/ModifyFilterQueryPartsEvent.php b/src/Event/ModifyFilterQueryPartsEvent.php index 0fd2c9cf..f8ce40f0 100644 --- a/src/Event/ModifyFilterQueryPartsEvent.php +++ b/src/Event/ModifyFilterQueryPartsEvent.php @@ -9,6 +9,7 @@ namespace HeimrichHannot\FilterBundle\Event; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; +use HeimrichHannot\FilterBundle\Processor\FilterContext; use Symfony\Component\EventDispatcher\Event; class ModifyFilterQueryPartsEvent extends Event @@ -21,11 +22,11 @@ class ModifyFilterQueryPartsEvent extends Event protected $partsCollection; /** - * @var array|null + * @var FilterContext */ protected $filter; - public function __construct(FilterQueryPartCollection $partsCollection, ?array $filter) + public function __construct(FilterQueryPartCollection $partsCollection, FilterContext $filter) { $this->partsCollection = $partsCollection; $this->filter = $filter; @@ -41,13 +42,8 @@ public function setPartsCollection(FilterQueryPartCollection $partsCollection): $this->partsCollection = $partsCollection; } - public function getFilter(): ?array + public function getFilter(): FilterContext { return $this->filter; } - - public function setFilter(array $filter): void - { - $this->filter = $filter; - } } diff --git a/src/EventListener/LoadDataContainerListener.php b/src/EventListener/LoadDataContainerListener.php index e76e75c1..a1a00f58 100644 --- a/src/EventListener/LoadDataContainerListener.php +++ b/src/EventListener/LoadDataContainerListener.php @@ -9,8 +9,8 @@ namespace HeimrichHannot\FilterBundle\EventListener; use Doctrine\DBAL\Connection; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; -use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; +use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; class LoadDataContainerListener { diff --git a/src/Filter/Type/ParentType.php b/src/Filter/Type/ParentType.php index 07d8e437..fda07eea 100644 --- a/src/Filter/Type/ParentType.php +++ b/src/Filter/Type/ParentType.php @@ -14,7 +14,7 @@ class ParentType extends ChoiceType { - const TYPE = 'parent'; + const TYPE = 'filterConfig'; /** {@inheritdoc} */ public function getChoices(FilterConfigElementModel $element) diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php index 04d8968a..1bbf1e27 100644 --- a/src/FilterQuery/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPart.php @@ -8,7 +8,7 @@ namespace HeimrichHannot\FilterBundle\FilterQuery; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; class FilterQueryPart { @@ -58,7 +58,7 @@ class FilterQueryPart private $overridable = true; /** - * @var string|int|array|\DateTime + * @var string|int|array|\DateTimeInterface */ private $value; @@ -79,7 +79,7 @@ public function __construct(FilterTypeContext $filterTypeContext) $this->name = $elementConfig->getElementName(); $this->filterElementId = $elementConfig->id; $this->operator = $elementConfig->operator; - $this->field = $filterTypeContext->getParent()->row()['dataContainer'].'.'.$elementConfig->field; + $this->field = $filterTypeContext->getFilterConfig()->row()['dataContainer'].'.'.$elementConfig->field; $this->wildcard = ':'.str_replace('.', '_', $elementConfig->field).'_'.$elementConfig->id; if ($elementConfig->isInitial) { @@ -105,7 +105,7 @@ public function setWildcard(string $wildcard): void } /** - * @return array|\DateTime|int|string + * @return array|\DateTimeInterface|int|string */ public function getValue() { @@ -113,7 +113,7 @@ public function getValue() } /** - * @param array|\DateTime|int|string $value + * @param array|\DateTimeInterface|int|string $value */ public function setValue($value): void { diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index a533a914..771c6e33 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -12,8 +12,8 @@ use Contao\StringUtil; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Date\DateUtil; @@ -205,7 +205,7 @@ function ($val) { return $where; } - public function updateInitialFilterProperties(FilterQueryPart &$filterPart): void + public function updateInitialFilterProperties(FilterQueryPart $filterPart): void { if (null === $filterPart->getInitialValue()) { return; diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 475c4717..bf4fa5ac 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -13,10 +13,10 @@ use Contao\System; use HeimrichHannot\FilterBundle\Config\FilterConfig; use HeimrichHannot\FilterBundle\Exception\MissingFilterConfigException; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeInterface; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\FilterTypeInterface; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\FormType; @@ -196,7 +196,7 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil } $context->setElementConfig($element); $context->setFormBuilder($builder); - $context->setParent($element->getRelated('pid')); + $context->setFilterConfig($element->getRelated('pid')); try { $filterType->buildForm($context); diff --git a/src/Manager/FilterManager.php b/src/Manager/FilterManager.php index 8856eaaf..88df5729 100644 --- a/src/Manager/FilterManager.php +++ b/src/Manager/FilterManager.php @@ -147,7 +147,7 @@ protected function getConfig(array $filter, $request = null) */ $adapter = $this->framework->getAdapter(FilterConfigElementModel::class); - // get the parent filter config + // get the filterConfig filter config if (isset($filter['type']) && FilterConfig::FILTER_TYPE_SORT === $filter['type']) { $parentFilter = $this->framework->getAdapter(FilterConfigModel::class)->findById($filter['parentFilter'])->row(); diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index d55743e7..f9ba6f8d 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -147,7 +147,7 @@ class FilterConfigElementModel extends Model implements \JsonSerializable protected $formName; /** - * Find published filte elements items by their parent ID. + * Find published filte elements items by their filterConfig ID. * * @param int $intId The filter ID * @param int $intLimit An optional limit @@ -184,7 +184,7 @@ public function findPublishedByPid($intId, $intLimit = 0, array $arrOptions = [] } /** - * Find published filter elements items by their parent ID and optional types. + * Find published filter elements items by their filterConfig ID and optional types. * * @param int $intId The filter ID * @param array $types The list of element types diff --git a/src/Processor/FilterContext.php b/src/Processor/FilterContext.php new file mode 100644 index 00000000..8c4c3a35 --- /dev/null +++ b/src/Processor/FilterContext.php @@ -0,0 +1,57 @@ +filterConfigModel = $filterConfigModel; + $this->formData = $formData; + } + + public function getInitialData() + { + } + + public function getFilterConfigModel(): FilterConfigModel + { + return $this->filterConfigModel; + } + + public function setFilterConfigModel(FilterConfigModel $filterConfigModel): void + { + $this->filterConfigModel = $filterConfigModel; + } + + public function getFormData(): array + { + return $this->formData; + } + + public function setFormData(array $formData): void + { + $this->formData = $formData; + } +} diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index f6506343..d91d2538 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -23,7 +23,7 @@ huh: - { name: proximity_search, class: HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType, type: other, wrapper: true } - { name: language, class: HeimrichHannot\FilterBundle\Filter\Type\LanguageType, type: choice } - { name: locale, class: HeimrichHannot\FilterBundle\Filter\Type\LocaleType, type: choice } - - { name: parent, class: HeimrichHannot\FilterBundle\Filter\Type\ParentType, type: choice } + - { name: filterConfig, class: HeimrichHannot\FilterBundle\Filter\Type\ParentType, type: choice } - { name: skip_parents, class: HeimrichHannot\FilterBundle\Filter\Type\SkipParentsType, type: other } - { name: visible, class: HeimrichHannot\FilterBundle\Filter\Type\PublishedType, type: other } - { name: button, class: HeimrichHannot\FilterBundle\Filter\Type\ButtonType, type: button } diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 594f1f79..53fb651d 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -167,15 +167,15 @@ services: tags: - { name: twig.extension } - HeimrichHannot\FilterBundle\FilterType\Type\: - resource: '../../FilterType/Type/*' - tags: ['huh.filter.filter_type'] - HeimrichHannot\FilterBundle\Filter\Filter: ~ - HeimrichHannot\FilterBundle\FilterType\FilterTypeCollection: + HeimrichHannot\FilterBundle\Type\Concrete\: + resource: '../../Type/Concrete/*' + tags: ['huh.filter.type.concrete'] + + HeimrichHannot\FilterBundle\Type\FilterTypeCollection: arguments: - - !tagged huh.filter.filter_type + - !tagged huh.filter.type.concrete HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection: ~ diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index 78d925d7..8ea4876a 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -52,6 +52,7 @@ $lang['customValue'] = ['Wert anpassen', 'Wählen Sie diese Option, um den Wert anzupassen.']; $lang['value'] = ['Wert', 'Geben Sie hier den gewünschten Wert ein.']; $lang['isInitial'] = ['Initiales Filterelement', 'Wählen Sie diese Option, um das Filterelement als "initial" zu kennzeichnen. Dadurch wird es im Frontend nicht ausgegeben, aber trotzdem angewendet. Normale Filterelemente überschreiben initiale Filterelemente.']; +$lang['isInitialOverridable'] = ['Überschreiben erlauben', 'Wählen Sie diese Option, damit es möglich ist diesen Initialen Filter zu überschreiben. Normale Filterelemente überschreiben diesen Filter wenn sie auf das gleiche DCA-Feld eingerichtet sind.']; $lang['initialValueType'] = ['Typ des initialen Werts', 'Wählen Sie hier den Typ des initialen Werts aus.']; $lang['initialValue'] = ['Initialer Wert', 'Legen Sie hier den initialen Wert fest.']; $lang['initialValue_value'] = ['Wert', '']; @@ -128,6 +129,7 @@ /* * Legends */ +$lang['initial_legend'] = 'Initiale Einstellungen'; $lang['general_legend'] = 'Allgemeine Einstellungen'; $lang['config_legend'] = 'Konfiguration'; $lang['visualization_legend'] = 'Darstellung'; @@ -173,7 +175,7 @@ \HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::TYPE => 'Umkreissuche', 'language' => 'Sprache', 'locale' => 'Region ("locale")', - 'parent' => 'Elternentität', + 'filterConfig' => 'Elternentität', 'skip_parents' => 'Elternentitäten ausschließen', 'visible' => 'Veröffentlicht', 'button' => 'Button - [deprecated]', @@ -196,10 +198,10 @@ \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE => 'aktuelles Mitglied', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE_ID => 'ID', \HeimrichHannot\FilterBundle\Filter\Type\CurrentMemberType::TYPE_USERNAME => 'Benutzername', - \HeimrichHannot\FilterBundle\FilterType\Type\TextType::TYPE => 'Text', - \HeimrichHannot\FilterBundle\FilterType\Type\ChoiceType::TYPE => 'Choice', - \HeimrichHannot\FilterBundle\FilterType\Type\DateTimeType::TYPE => 'Datum & Zeit', - \HeimrichHannot\FilterBundle\FilterType\Type\ButtonType::TYPE => 'Button', + \HeimrichHannot\FilterBundle\Type\Concrete\TextType::TYPE => 'Text', + \HeimrichHannot\FilterBundle\Type\Concrete\ChoiceType::TYPE => 'Choice', + \HeimrichHannot\FilterBundle\Type\Concrete\DateTimeType::TYPE => 'Datum & Zeit', + \HeimrichHannot\FilterBundle\Type\Concrete\ButtonType::TYPE => 'Button', ], 'roundingMode' => [ \Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer::ROUND_DOWN => 'Abrunden (zu 0 hin)', diff --git a/src/Resources/contao/languages/en/tl_filter_config_element.php b/src/Resources/contao/languages/en/tl_filter_config_element.php index 3eec41b1..51597f4a 100644 --- a/src/Resources/contao/languages/en/tl_filter_config_element.php +++ b/src/Resources/contao/languages/en/tl_filter_config_element.php @@ -1,7 +1,7 @@ 'Country', 'language' => 'Language', 'locale' => 'Locale', - 'parent' => 'Parent entity', + 'filterConfig' => 'Parent entity', 'published' => 'Published', 'button' => 'Button', 'reset' => 'Reset', diff --git a/src/FilterType/AbstractFilterType.php b/src/Type/AbstractFilterType.php similarity index 99% rename from src/FilterType/AbstractFilterType.php rename to src/Type/AbstractFilterType.php index 90e663c5..25d685f9 100644 --- a/src/FilterType/AbstractFilterType.php +++ b/src/Type/AbstractFilterType.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; diff --git a/src/FilterType/Type/ButtonType.php b/src/Type/Concrete/ButtonType.php similarity index 90% rename from src/FilterType/Type/ButtonType.php rename to src/Type/Concrete/ButtonType.php index 66677023..d9faf2e1 100644 --- a/src/FilterType/Type/ButtonType.php +++ b/src/Type/Concrete/ButtonType.php @@ -6,10 +6,10 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType\Type; +namespace HeimrichHannot\FilterBundle\Type\Concrete; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use Symfony\Component\Form\Extension\Core\Type\ButtonType as SymfonyButtonType; use Symfony\Component\Form\Extension\Core\Type\SubmitType as SymfonySubmitType; diff --git a/src/FilterType/Type/ChoiceType.php b/src/Type/Concrete/ChoiceType.php similarity index 93% rename from src/FilterType/Type/ChoiceType.php rename to src/Type/Concrete/ChoiceType.php index 47de22fd..376b6d2a 100644 --- a/src/FilterType/Type/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -6,14 +6,14 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType\Type; +namespace HeimrichHannot\FilterBundle\Type\Concrete; use Doctrine\DBAL\Driver\Connection; use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; @@ -92,7 +92,7 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $options['expanded'] = $elementConfig->expanded; if ((bool) $elementConfig->submitOnChange) { - if ($filterTypeContext->getParent()->asyncFormSubmit) { + if ($filterTypeContext->getFilterConfig()->asyncFormSubmit) { $options['attr']['data-submit-on-change'] = 1; } else { if ($options['expanded']) { @@ -137,7 +137,7 @@ public function collectChoices(FilterTypeContext $filterTypeContext): array return $this->fieldOptionsChoice->getCachedChoices([ 'element' => $filterTypeContext->getElementConfig(), - 'filter' => $filterTypeContext->getParent()->row(), + 'filter' => $filterTypeContext->getFilterConfig()->row(), ]); } } diff --git a/src/FilterType/Type/DateTimeType.php b/src/Type/Concrete/DateTimeType.php similarity index 96% rename from src/FilterType/Type/DateTimeType.php rename to src/Type/Concrete/DateTimeType.php index e4826a54..a4b9d8be 100644 --- a/src/FilterType/Type/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -6,15 +6,15 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType\Type; +namespace HeimrichHannot\FilterBundle\Type\Concrete; use Contao\Date; use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; -use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Date\DateUtil; use Symfony\Component\Form\Extension\Core\Type\DateTimeType as SymfonyDateTimeType; diff --git a/src/FilterType/Type/TextType.php b/src/Type/Concrete/TextType.php similarity index 82% rename from src/FilterType/Type/TextType.php rename to src/Type/Concrete/TextType.php index 96341edb..75157936 100644 --- a/src/FilterType/Type/TextType.php +++ b/src/Type/Concrete/TextType.php @@ -6,12 +6,12 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType\Type; +namespace HeimrichHannot\FilterBundle\Type\Concrete; -use HeimrichHannot\FilterBundle\FilterType\AbstractFilterType; -use HeimrichHannot\FilterBundle\FilterType\FilterTypeContext; -use HeimrichHannot\FilterBundle\FilterType\InitialFilterTypeInterface; -use HeimrichHannot\FilterBundle\FilterType\PlaceholderFilterTypeInterface; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; +use HeimrichHannot\FilterBundle\Type\PlaceholderFilterTypeInterface; use Symfony\Component\Form\Extension\Core\Type\TextType as SymfonyTextType; class TextType extends AbstractFilterType implements InitialFilterTypeInterface, PlaceholderFilterTypeInterface @@ -60,7 +60,7 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $elementConfig = $filterTypeContext->getElementConfig(); - if ((bool) $elementConfig->submitOnInput && (bool) $filterTypeContext->getParent()->row()['asyncFormSubmit']) { + if ((bool) $elementConfig->submitOnInput && (bool)$filterTypeContext->getFilterConfig()->row()['asyncFormSubmit']) { $options['attr']['data-submit-on-input'] = '1'; $options['attr']['data-threshold'] = $elementConfig->threshold ?: '0'; $options['attr']['data-debounce'] = $elementConfig->debounce ?: '0'; diff --git a/src/FilterType/FilterTypeCollection.php b/src/Type/FilterTypeCollection.php similarity index 96% rename from src/FilterType/FilterTypeCollection.php rename to src/Type/FilterTypeCollection.php index a22f8827..06235db1 100644 --- a/src/FilterType/FilterTypeCollection.php +++ b/src/Type/FilterTypeCollection.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; class FilterTypeCollection { diff --git a/src/FilterType/FilterTypeContext.php b/src/Type/FilterTypeContext.php similarity index 80% rename from src/FilterType/FilterTypeContext.php rename to src/Type/FilterTypeContext.php index 787ed70e..91ac5561 100644 --- a/src/FilterType/FilterTypeContext.php +++ b/src/Type/FilterTypeContext.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; use Contao\Model; use DateTimeInterface; @@ -28,10 +28,10 @@ class FilterTypeContext /** * @var Model */ - private $parent = null; + private $filterConfig = null; /** - * @var string|array|int|DateTimeInterface + * @var mixed */ private $value; @@ -41,7 +41,7 @@ class FilterTypeContext private $valueType; /** - * @return array|string|int|DateTimeInterface + * @return mixed */ public function getValue() { @@ -49,7 +49,7 @@ public function getValue() } /** - * @param string|array|int|DateTimeInterface $value + * @param mixed $value */ public function setValue($value): void { @@ -59,14 +59,14 @@ public function setValue($value): void /** * @return Model */ - public function getParent(): ?Model + public function getFilterConfig(): ?Model { - return $this->parent; + return $this->filterConfig; } - public function setParent(?Model $parent): void + public function setFilterConfig(?Model $filterConfig): void { - $this->parent = $parent; + $this->filterConfig = $filterConfig; } public function getFormBuilder(): FormBuilderInterface diff --git a/src/FilterType/FilterTypeInterface.php b/src/Type/FilterTypeInterface.php similarity index 88% rename from src/FilterType/FilterTypeInterface.php rename to src/Type/FilterTypeInterface.php index 1397d0b3..b81ff02b 100644 --- a/src/FilterType/FilterTypeInterface.php +++ b/src/Type/FilterTypeInterface.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; interface FilterTypeInterface { diff --git a/src/FilterType/InitialFilterTypeInterface.php b/src/Type/InitialFilterTypeInterface.php similarity index 85% rename from src/FilterType/InitialFilterTypeInterface.php rename to src/Type/InitialFilterTypeInterface.php index 631665f2..4cc4fd61 100644 --- a/src/FilterType/InitialFilterTypeInterface.php +++ b/src/Type/InitialFilterTypeInterface.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; interface InitialFilterTypeInterface { diff --git a/src/FilterType/PlaceholderFilterTypeInterface.php b/src/Type/PlaceholderFilterTypeInterface.php similarity index 78% rename from src/FilterType/PlaceholderFilterTypeInterface.php rename to src/Type/PlaceholderFilterTypeInterface.php index 6fd1b987..78ca7d5c 100644 --- a/src/FilterType/PlaceholderFilterTypeInterface.php +++ b/src/Type/PlaceholderFilterTypeInterface.php @@ -6,7 +6,7 @@ * @license LGPL-3.0-or-later */ -namespace HeimrichHannot\FilterBundle\FilterType; +namespace HeimrichHannot\FilterBundle\Type; interface PlaceholderFilterTypeInterface { diff --git a/tests/ContaoManager/PluginTest.php b/tests/ContaoManager/PluginTest.php index 2461d7a3..9607923f 100644 --- a/tests/ContaoManager/PluginTest.php +++ b/tests/ContaoManager/PluginTest.php @@ -112,7 +112,7 @@ public function testGetExtensionConfigLoadFilterConfig() $this->assertContains(['name' => 'country', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\CountryType', 'type' => 'choice'], $extensionConfigs['huh']['filter']['types']); $this->assertContains(['name' => 'language', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\LanguageType', 'type' => 'choice'], $extensionConfigs['huh']['filter']['types']); $this->assertContains(['name' => 'locale', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\LocaleType', 'type' => 'choice'], $extensionConfigs['huh']['filter']['types']); - $this->assertContains(['name' => 'parent', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\ParentType', 'type' => 'choice'], $extensionConfigs['huh']['filter']['types']); + $this->assertContains(['name' => 'filterConfig', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\ParentType', 'type' => 'choice'], $extensionConfigs['huh']['filter']['types']); $this->assertContains(['name' => 'button', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\ButtonType', 'type' => 'button'], $extensionConfigs['huh']['filter']['types']); $this->assertContains(['name' => 'reset', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\ResetType', 'type' => 'button'], $extensionConfigs['huh']['filter']['types']); $this->assertContains(['name' => 'submit', 'class' => 'HeimrichHannot\FilterBundle\Filter\Type\SubmitType', 'type' => 'button'], $extensionConfigs['huh']['filter']['types']); diff --git a/tests/Filter/Type/ParentTypeTest.php b/tests/Filter/Type/ParentTypeTest.php index 90232ae3..9a269515 100644 --- a/tests/Filter/Type/ParentTypeTest.php +++ b/tests/Filter/Type/ParentTypeTest.php @@ -248,7 +248,7 @@ public function testBuildFormWithFieldName() 'filter' => [ 'types' => [ [ - 'name' => 'parent', + 'name' => 'filterConfig', 'class' => ParentType::class, 'type' => 'choice', ], @@ -262,7 +262,7 @@ public function testBuildFormWithFieldName() $filter = ['name' => 'test', 'dataContainer' => 'tl_test']; $element = new FilterConfigElementModel(); - $element->type = 'parent'; + $element->type = 'filterConfig'; $element->field = 'test'; $config->init('test', $filter, [$element]); From 6a58d73cf8d2a0276fe542174e318cc3ec265729 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 29 Apr 2021 12:16:03 +0200 Subject: [PATCH 34/58] refactored deprecated options on type dropdown --- .../FilterConfigElementContainer.php | 17 +++++++++++++++-- src/DependencyInjection/Configuration.php | 6 +++--- src/Resources/config/config.yml | 2 +- .../languages/de/tl_filter_config_element.php | 13 +++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 66316cd7..06ec99ae 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -89,6 +89,19 @@ public function onTypeOptionsCallback(DataContainer $dc): array } } + // separate deprecated types + $options['deprecated'] = []; + + foreach ($this->bundleConfig['filter']['deprecated_types'] as $deprecated) { + foreach ($options as $key => $option) { + if (\in_array($deprecated, $option)) { + $helperKey = array_search($deprecated, $option); + unset($options[$key][$helperKey]); + $options['deprecated'][] = $deprecated; + } + } + } + return $options; } @@ -115,7 +128,7 @@ public function onInitialValueTypeCallback(DC_Table $dc): array public function onOperatorOptionsCallback(DataContainer $dc) { - if (!$this->bundleConfig['filter']['disable_legacy_filters']) { + if (null === $this->typeCollection->getType($dc->activeRecord->type)) { return DatabaseUtil::OPERATORS; } @@ -135,7 +148,7 @@ public function onPlaceholderOptionsCallback(DataContainer $dc): array public function onDateWidgetOptionsCallback(DataContainer $dc): array { - if ($this->bundleConfig['filter']['disable_legacy_filers']) { + if (null === $this->typeCollection->getType($dc->activeRecord->type)) { return [ \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_CHOICE, \HeimrichHannot\FilterBundle\Filter\Type\DateType::WIDGET_TYPE_TEXT, diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 58cf5abc..cbd7a87b 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -39,9 +39,9 @@ public function getConfigTreeBuilder() ->arrayNode('filter') ->addDefaultsIfNotSet() ->children() - ->booleanNode('disable_legacy_filters') - ->defaultFalse() - ->info('Disable legacy filters to be able to use new implementation of filters together with HTTP GET requests.') + ->arrayNode('deprecated_types') + ->scalarPrototype()->end() + ->info('Add deprecated types here.') ->end() ->arrayNode('types') ->arrayPrototype() diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index d91d2538..2a1681bf 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,6 +1,6 @@ huh: filter: -# disable_legacy_filters: true + deprecated_types: ['button', 'reset', 'submit', 'text', 'choice', 'date_time'] types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index 8ea4876a..e42c7c7e 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -153,8 +153,9 @@ */ $lang['reference'] = [ 'type' => [ + 'deprecated' => 'Veraltet', 'miscellaneous' => 'Sonstiges', - 'text' => 'Text - [deprecated]', + 'text' => 'Text', 'text_concat' => 'Konkatenierter Text', 'textarea' => 'Textarea', \HeimrichHannot\FilterBundle\Filter\Type\EmailType::TYPE => 'E-Mail', @@ -169,7 +170,7 @@ \HeimrichHannot\FilterBundle\Filter\Type\MultipleRangeType::TYPE => 'Multi-Feld-Spanne (range)', 'tel' => 'Telefon', 'color' => 'Farbe', - 'choice' => 'Choice - [deprecated]', + 'choice' => 'Choice', \HeimrichHannot\FilterBundle\Filter\Type\RadiusChoiceType::TYPE => 'Radius-Choice', 'country' => 'Land', \HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::TYPE => 'Umkreissuche', @@ -178,15 +179,15 @@ 'filterConfig' => 'Elternentität', 'skip_parents' => 'Elternentitäten ausschließen', 'visible' => 'Veröffentlicht', - 'button' => 'Button - [deprecated]', - 'reset' => 'Reset - [deprecated]', - 'submit' => 'Submit - [deprecated]', + 'button' => 'Button', + 'reset' => 'Reset', + 'submit' => 'Submit', 'hidden' => 'Hidden', 'checkbox' => 'Checkbox', 'radio' => 'Radio', 'other' => 'Sonstiges', 'initial' => 'Initial', - \HeimrichHannot\FilterBundle\Filter\Type\DateTimeType::TYPE => 'Datum & Zeit - [deprecated]', + \HeimrichHannot\FilterBundle\Filter\Type\DateTimeType::TYPE => 'Datum & Zeit', \HeimrichHannot\FilterBundle\Filter\Type\DateType::TYPE => 'Datum', 'time' => 'Zeit', \HeimrichHannot\FilterBundle\Filter\Type\DateRangeType::TYPE => 'Datumsspanne (date range)', From 345873634c0b5a8ab28e247cebcf9ccda98a5275 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 29 Apr 2021 12:31:42 +0200 Subject: [PATCH 35/58] refactored defaultOperator for legacy types --- src/Config/FilterConfig.php | 16 ++++++++++------ .../languages/de/tl_filter_config_element.php | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 54db7437..657a1480 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -30,7 +30,6 @@ use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\FilterBundle\Type\FilterTypeInterface; -use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -734,11 +733,16 @@ protected function processLegacyFilterType(FilterConfigElementModel $config, arr return; } - if ('' === $config->operator && '' !== $config->customOperator) { - $config->operator = $config->customOperator; - } elseif ('' === $config->operator && '' === $config->customOperator) { - //TODO: get from Class - $config->operator = DatabaseUtil::OPERATOR_EQUAL; + if ('' === $config->customOperator || ('1' === $config->customOperator && '' === $config->operator)) { + $class = $element['class']; + + if (!class_exists($class)) { + return; + } + + /** @var AbstractType $type */ + $type = new $class($this); + $config->operator = $type->getDefaultOperator($config); } $context = new FilterTypeContext(); diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index e42c7c7e..21e3201c 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -153,7 +153,7 @@ */ $lang['reference'] = [ 'type' => [ - 'deprecated' => 'Veraltet', + 'deprecated' => 'Veraltet - NICHT nutzen', 'miscellaneous' => 'Sonstiges', 'text' => 'Text', 'text_concat' => 'Konkatenierter Text', From 248758c7e1efe2935341092651697e83d5babc4a Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 29 Apr 2021 16:50:43 +0200 Subject: [PATCH 36/58] fixed error not retrieving correct pageId on multiple following async submits --- src/Config/FilterConfig.php | 2 +- src/Controller/FrontendFilterController.php | 21 --------------------- src/Form/FilterType.php | 16 +++++++++++++--- 3 files changed, 14 insertions(+), 25 deletions(-) delete mode 100644 src/Controller/FrontendFilterController.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 657a1480..77436fb1 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -733,7 +733,7 @@ protected function processLegacyFilterType(FilterConfigElementModel $config, arr return; } - if ('' === $config->customOperator || ('1' === $config->customOperator && '' === $config->operator)) { + if ('' === $config->customOperator || (true === (bool) $config->customOperator && false === (bool) $config->operator)) { $class = $element['class']; if (!class_exists($class)) { diff --git a/src/Controller/FrontendFilterController.php b/src/Controller/FrontendFilterController.php deleted file mode 100644 index 3140e016..00000000 --- a/src/Controller/FrontendFilterController.php +++ /dev/null @@ -1,21 +0,0 @@ -get($filter['name'])[self::FILTER_PAGE_ID_NAME]; + + if (is_numeric($pageId)) { + $objPage = System::getContainer()->get(PageUtil::class)->retrieveGlobalPageFromCurrentPageId((int) $pageId); + } + } + $builder->add(static::FILTER_PAGE_ID_NAME, HiddenType::class, ['attr' => ['value' => $objPage->id]]); // always add a hidden field with the referrer url (required by reset for example to redirect back to user action page) -> use request query string when in esi _ fragment sub-request @@ -117,10 +127,10 @@ protected function buildElements(FormBuilderInterface $builder, array $options) } $wrappers = []; - $types = System::getContainer()->get('huh.filter.choice.type')->getCachedChoices(); + $legacyTypes = System::getContainer()->get('huh.filter.choice.type')->getCachedChoices(); - $newTypes = System::getContainer()->get(FilterTypeCollection::class)->getTypes(); - $types = array_merge($types, $newTypes); + $types = System::getContainer()->get(FilterTypeCollection::class)->getTypes(); + $types = array_merge($legacyTypes, $types); if (!\is_array($types) || empty($types)) { return; From a17c2d26d87bc6f9c83642e73c2ed546b3a47f16 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Thu, 29 Apr 2021 17:21:46 +0200 Subject: [PATCH 37/58] refactored is null issue fix --- src/Form/FilterType.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 7a49b16a..19fca0e8 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -17,7 +17,6 @@ use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\FilterBundle\Type\FilterTypeInterface; -use HeimrichHannot\UtilsBundle\Page\PageUtil; use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\FormType; @@ -72,15 +71,14 @@ public function buildForm(FormBuilderInterface $builder, array $options) // always add a hidden field with the page id global $objPage; - if (null === $objPage) { - $pageId = $request->get($filter['name'])[self::FILTER_PAGE_ID_NAME]; + $pageId = $objPage->id; - if (is_numeric($pageId)) { - $objPage = System::getContainer()->get(PageUtil::class)->retrieveGlobalPageFromCurrentPageId((int) $pageId); - } + // if $objPage is null (i.e. on AjaxRequest), get the page id from Request + if (null === $objPage) { + $pageId = $request->get($filter['name'])[static::FILTER_PAGE_ID_NAME]; } - $builder->add(static::FILTER_PAGE_ID_NAME, HiddenType::class, ['attr' => ['value' => $objPage->id]]); + $builder->add(static::FILTER_PAGE_ID_NAME, HiddenType::class, ['attr' => ['value' => $pageId]]); // always add a hidden field with the referrer url (required by reset for example to redirect back to user action page) -> use request query string when in esi _ fragment sub-request if ($request->query->has('request')) { From b0274629de2c1108e4d3e7b3a7bd643e9052744f Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 30 Apr 2021 11:34:16 +0200 Subject: [PATCH 38/58] fixed async form submit, adjusted choiceType for form reset --- src/Controller/FrontendAjaxController.php | 44 ++++++++++++++----- src/Form/FilterType.php | 22 +++++++++- .../npm-package/js/contao-filter-bundle.js | 8 ++-- src/Type/Concrete/ButtonType.php | 9 ++++ src/Type/Concrete/ChoiceType.php | 7 +-- 5 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/Controller/FrontendAjaxController.php b/src/Controller/FrontendAjaxController.php index e77be830..9cb1b351 100644 --- a/src/Controller/FrontendAjaxController.php +++ b/src/Controller/FrontendAjaxController.php @@ -17,6 +17,7 @@ use HeimrichHannot\FilterBundle\Form\FilterType; use HeimrichHannot\UtilsBundle\Page\PageUtil; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\Form\FormBuilder; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -55,26 +56,26 @@ public function ajaxSubmitAction(Request $request, int $id): Response { $this->get('contao.framework')->initialize(); - if (null === ($filter = $this->get('huh.filter.manager')->findById($id))) { + if (null === ($filterConfig = $this->get('huh.filter.manager')->findById($id))) { throw new MissingFilterException('A filter with id '.$id.' does not exist.'); } - if (null === ($response = $filter->handleForm())) { + if (null === ($response = $filterConfig->handleForm())) { throw new HandleFormException('Unable to handle form for filter with id '.$id.'.'); } - if ($request->get($filter->getFilter()['name']) && isset($request->get($filter->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME])) { - if (parse_url($request->get($filter->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME], PHP_URL_HOST) !== parse_url(Environment::get('url'), PHP_URL_HOST)) { + if ($request->get($filterConfig->getFilter()['name']) && isset($request->get($filterConfig->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME])) { + if (parse_url($request->get($filterConfig->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME], PHP_URL_HOST) !== parse_url(Environment::get('url'), PHP_URL_HOST)) { throw new \Exception('Invalid redirect url'); } - Environment::set('request', $request->get($filter->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME]); + Environment::set('request', $request->get($filterConfig->getFilter()['name'])[FilterType::FILTER_REFERRER_NAME]); } global $objPage; if (null === $objPage) { - $pageId = $request->get($filter->getFilter()['name'])[FilterType::FILTER_PAGE_ID_NAME]; + $pageId = $request->get($filterConfig->getFilter()['name'])[FilterType::FILTER_PAGE_ID_NAME]; if (is_numeric($pageId)) { $objPage = $this->pageUtil->retrieveGlobalPageFromCurrentPageId((int) $pageId); @@ -85,11 +86,31 @@ public function ajaxSubmitAction(Request $request, int $id): Response $response = new JsonResponse(); - if (null === $filter->getBuilder()) { - $filter->buildForm($filter->getData()); + if (null === $filterConfig->getBuilder()) { + $filterConfig->buildForm($filterConfig->getData()); } - $builder = $filter->getBuilder(); + /** @var FormBuilder $builder */ + $builder = $filterConfig->getBuilder(); + + if ($request->isMethod('GET')) { + if (null !== $request->query->get('button_clicked')) { + if (\in_array($request->query->get('button_clicked'), $filterConfig->getResetNames())) { + $filterConfig->resetData(); + } + } + } + + if ($request->isMethod('POST')) { + if (null !== $request->request->get('button_clicked')) { + if (\in_array($request->request->get('button_clicked'), $filterConfig->getResetNames())) { + $filterConfig->resetData(); + } + } + } + + $builder->setData($filterConfig->getData()); + $form = $builder->getForm(); /** @@ -97,11 +118,10 @@ public function ajaxSubmitAction(Request $request, int $id): Response */ $twig = System::getContainer()->get('twig'); - $filterConfig = $filter; $filter = $twig->render( - $filter->getFilterTemplateByName($filter->getFilter()['template']), + $filterConfig->getFilterTemplateByName($filterConfig->getFilter()['template']), [ - 'filter' => $filter, + 'filter' => $filterConfig, 'form' => $form->createView(), ] ); diff --git a/src/Form/FilterType.php b/src/Form/FilterType.php index 19fca0e8..a602968d 100644 --- a/src/Form/FilterType.php +++ b/src/Form/FilterType.php @@ -199,9 +199,29 @@ protected function buildFilterTypeElement(FilterConfigElementModel $element, Fil $request = Request::createFromGlobals(); $context = new FilterTypeContext(); + /** @var FilterConfig $filter */ + $filter = $builder->getOptions()['filter']; + + if ($request->isMethod('GET')) { + if (null !== $request->query->get('button_clicked')) { + if (\in_array($request->query->get('button_clicked'), $filter->getResetNames())) { + $filter->resetData(); + } + } + } + + if ($request->isMethod('POST')) { + if (null !== $request->request->get('button_clicked')) { + if (\in_array($request->request->get('button_clicked'), $filter->getResetNames())) { + $filter->resetData(); + } + } + } + if (null !== $request->query->get($element->getRelated('pid')->name)[$element->getElementName()]) { - $context->setValue($request->query->get($element->getRelated('pid')->name)[$element->getElementName()]); + $context->setValue($filter->getData()[$element->getElementName()]); } + $context->setElementConfig($element); $context->setFormBuilder($builder); $context->setFilterConfig($element->getRelated('pid')); diff --git a/src/Resources/npm-package/js/contao-filter-bundle.js b/src/Resources/npm-package/js/contao-filter-bundle.js index 58e11333..138aee28 100644 --- a/src/Resources/npm-package/js/contao-filter-bundle.js +++ b/src/Resources/npm-package/js/contao-filter-bundle.js @@ -89,7 +89,6 @@ class FilterBundle { static initAsyncSubmitOnInput() { let timeout; - EventUtil.addDynamicEventListener('input', '.mod_filter form[data-async] input[data-submit-on-input], .mod_filter form[data-async] [data-submit-on-input] input', function(element, event) { @@ -117,8 +116,7 @@ class FilterBundle { static initAsyncFormSubmit(element) { let clickedButton = document.createElement('div'); - clickedButton.setAttribute('name', element.form.name + '[submit]') ; - + clickedButton.setAttribute('name', element.form.name + '[submit]'); FilterBundle.asyncSubmit(element.form, clickedButton); } @@ -130,6 +128,10 @@ class FilterBundle { if (clickedButton !== null) { data.append(clickedButton.getAttribute('name'), ''); + + if (clickedButton.hasAttribute('data-name')) { + data.append('button_clicked', clickedButton.dataset.name); + } } if ('get' === method || 'GET' === method) { diff --git a/src/Type/Concrete/ButtonType.php b/src/Type/Concrete/ButtonType.php index d9faf2e1..cee8befc 100644 --- a/src/Type/Concrete/ButtonType.php +++ b/src/Type/Concrete/ButtonType.php @@ -63,4 +63,13 @@ public function getPalette(string $prependPalette, string $appendPalette): strin { return $prependPalette.'{config_legend},buttonType;{visualization_legend},customLabel;'.$appendPalette; } + + public function getOptions(FilterTypeContext $filterTypeContext): array + { + $options = parent::getOptions($filterTypeContext); + + $options['attr']['data-name'] = $filterTypeContext->getElementConfig()->getElementName(); + + return $options; + } } diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 376b6d2a..54054c95 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -115,12 +115,13 @@ public function getOptions(FilterTypeContext $filterTypeContext): array } $options['multiple'] = $elementConfig->multiple; - $options['data'] = $filterTypeContext->getValue(); // forgiving array handling - if ((bool) $elementConfig->multiple && isset($options['data'])) { - $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; + if ($elementConfig->addDefaultValue) { + if (isset($options['multiple']) && true === (bool) $options['multiple'] && isset($options['data'])) { + $options['data'] = !\is_array($options['data']) ? [$options['data']] : $options['data']; + } } return $options; From eeb386c6ae9c6c56712f3e8e3959863d214e1225 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 30 Apr 2021 11:47:29 +0200 Subject: [PATCH 39/58] added github actions --- .github/workflows/ci.yml | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..08a2fe8d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,56 @@ +name: CI + +on: [ push ] + +jobs: + tests: + name: PHP ${{ matrix.php }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: [ 7.1, 7.2., 7.3, 7.4 ] + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, pcre, pdo, zlib + tools: phpunit + coverage: none + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install the dependencies + run: composer install --no-interaction + + - name: Run the unit tests + run: php vendor/bin/phpunit -c phpunit.xml.dist --colors=always + + coverage: + runs-on: ubuntu-latest + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + extensions: dom, fileinfo, filter, gd, hash, intl, json, mbstring, pcre, pdo, zlib + coverage: xdebug + tools: php-cs-fixer, phpunit + + - name: Checkout + uses: actions/checkout@v2 + + - name: Install the dependencies + run: composer install --no-interaction + + - name: Generate the coverage report + run: php vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover build/logs/clover.xml + + - name: Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer global require php-coveralls/php-coveralls + php-coveralls --coverage_clover=build/logs/clover.xml -v \ No newline at end of file From 1d4dc64bf4576a6b721af6eb9fe676ced53b0a2e Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 3 May 2021 16:47:39 +0200 Subject: [PATCH 40/58] added tests for TextType and FilterQueryPart --- phpunit.xml.dist | 1 + src/Config/FilterConfig.php | 3 +- src/FilterQuery/FilterQueryPart.php | 1 + tests/FilterQuery/FilterQueryPartTest.php | 117 ++++++++++++++ tests/ModelMockTrait.php | 65 ++++++++ tests/Type/Concrete/TextTypeTest.php | 180 ++++++++++++++++++++++ 6 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 tests/FilterQuery/FilterQueryPartTest.php create mode 100644 tests/ModelMockTrait.php create mode 100644 tests/Type/Concrete/TextTypeTest.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 661bfb28..87b2996e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,7 @@ + diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 77436fb1..043ad6f6 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -12,7 +12,6 @@ use Contao\CoreBundle\Framework\ContaoFrameworkInterface; use Contao\Environment; use Contao\InsertTags; -use Contao\Model; use Doctrine\DBAL\Connection; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; @@ -407,7 +406,7 @@ public function handleForm($request = null): ?RedirectResponse $form->get(FilterType::FILTER_REFERRER_NAME)->getData() ?: null); } - if (parse_url($url, PHP_URL_HOST) !== parse_url(Environment::get('url'), PHP_URL_HOST)) { + if (parse_url($url, \PHP_URL_HOST) !== parse_url(Environment::get('url'), \PHP_URL_HOST)) { throw new \Exception('Invalid redirect url'); } diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php index 1bbf1e27..b99e8b92 100644 --- a/src/FilterQuery/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPart.php @@ -87,6 +87,7 @@ public function __construct(FilterTypeContext $filterTypeContext) $this->initialValue = $elementConfig->initialValue ?: $elementConfig->initialValueArray; $this->initialValueType = $elementConfig->initialValueType; $this->value = $this->initialValue; + $this->valueType = $this->initialValueType; $this->overridable = $elementConfig->isInitialOverridable; } else { $this->value = $filterTypeContext->getValue(); diff --git a/tests/FilterQuery/FilterQueryPartTest.php b/tests/FilterQuery/FilterQueryPartTest.php new file mode 100644 index 00000000..ac340b5a --- /dev/null +++ b/tests/FilterQuery/FilterQueryPartTest.php @@ -0,0 +1,117 @@ +framework = $this->mockContaoFramework(); + $this->container = $this->mockContainer(); + } + + public function createTestInstance(array $parameters = [], $mockBuilder = false) + { + $filterTypeContext = $parameters['context'] ?? $this->createMock(FilterTypeContext::class); + + if ($mockBuilder) { + $instance = $this->getMockBuilder(FilterQueryPart::class) + ->setConstructorArgs([$filterTypeContext]) + ->getMock(); + } else { + $instance = new FilterQueryPart($filterTypeContext); + } + + return $instance; + } + + public function testGetterSetter() + { + System::setContainer($this->container); + + $context = new FilterTypeContext(); + $filterConfigModel = $this->mockModelObject(FilterConfigModel::class, ['dataContainer' => 'tl_news']); + $context->setFilterConfig($filterConfigModel); + + $elementConfigProps = [ + 'id' => 70, + 'type' => 'choice', + 'field' => 'content', + 'operator' => 'unequal', + 'isInitial' => false, + ]; + $elementConfigModel = $this->mockModelObject(FilterConfigElementModel::class, $elementConfigProps); + $elementConfigModel->method('getElementName')->willReturn('choice_70'); + $context->setElementConfig($elementConfigModel); + $context->setValueType(Types::INTEGER); + $context->setValue(7); + + $instance = $this->createTestInstance(['context' => $context]); + $this->assertSame('choice_70', $instance->getName()); + $this->assertSame(70, $instance->getFilterElementId()); + $this->assertSame('unequal', $instance->getOperator()); + $this->assertSame('tl_news.content', $instance->getField()); + $this->assertSame(':content_70', $instance->getWildcard()); + $this->assertFalse($instance->isInitial()); + $this->assertSame(7, $instance->getValue()); + $this->assertSame(Types::INTEGER, $instance->getValueType()); + $this->assertFalse($instance->isDisabled()); + + $elementConfigInitialProps = [ + 'id' => 70, + 'type' => 'text', + 'field' => 'teaser', + 'operator' => 'equal', + 'isInitial' => true, + 'initialValue' => 'text', + 'initialValueType' => Types::STRING, + 'isInitialOverridable' => false, + ]; + $elementConfigModel = $this->mockModelObject(FilterConfigElementModel::class, $elementConfigInitialProps); + $elementConfigModel->method('getElementName')->willReturn('text_70'); + $context->setElementConfig($elementConfigModel); + $instance = $this->createTestInstance(['context' => $context]); + + $this->assertSame('text_70', $instance->getName()); + $this->assertSame(70, $instance->getFilterElementId()); + $this->assertSame('equal', $instance->getOperator()); + $this->assertSame('tl_news.teaser', $instance->getField()); + $this->assertSame(':teaser_70', $instance->getWildcard()); + $this->assertTrue($instance->isInitial()); + $this->assertSame('text', $instance->getInitialValue()); + $this->assertSame(Types::STRING, $instance->getInitialValueType()); + $this->assertSame('text', $instance->getValue()); + $this->assertFalse($instance->isOverridable()); + $this->assertFalse($instance->isDisabled()); + } +} diff --git a/tests/ModelMockTrait.php b/tests/ModelMockTrait.php new file mode 100644 index 00000000..33611859 --- /dev/null +++ b/tests/ModelMockTrait.php @@ -0,0 +1,65 @@ +createMock($class); + $mock + ->method('__get') + ->willReturnCallback( + static function (string $key) use (&$properties) { + return $properties[$key] ?? null; + } + ) + ; + + if (\in_array('__set', get_class_methods($class), true)) { + $mock + ->method('__set') + ->willReturnCallback( + static function (string $key, $value) use (&$properties) { + $properties[$key] = $value; + } + ) + ; + } + + if (\in_array('__isset', get_class_methods($class), true)) { + $mock + ->method('__isset') + ->willReturnCallback( + static function (string $key) use (&$properties) { + return isset($properties[$key]); + } + ) + ; + } + + if (\in_array('row', get_class_methods($class), true)) { + $mock + ->method('row') + ->willReturnCallback( + static function () use (&$properties) { + return $properties; + } + ) + ; + } + + return $mock; + } +} diff --git a/tests/Type/Concrete/TextTypeTest.php b/tests/Type/Concrete/TextTypeTest.php new file mode 100644 index 00000000..a24848e8 --- /dev/null +++ b/tests/Type/Concrete/TextTypeTest.php @@ -0,0 +1,180 @@ +createMock(TranslatorInterface::class); + $processor = $parameters['processor'] ?? $this->createMock(FilterQueryPartProcessor::class); + $collection = $parameters['collection'] ?? $this->createMock(FilterQueryPartCollection::class); + + if ($mockBuilder) { + $instance = $this->getMockBuilder(TextType::class) + ->setConstructorArgs([$processor, $collection, $translator]) + ->setMethods(['getOptions', 'buildForm']) + ->getMock(); + } else { + $instance = new TextType($processor, $collection, $translator); + } + + return $instance; + } + + public function testGetType() + { + $instance = $this->createTestInstance(); + $this->assertSame('text_type', $instance->getType()); + } + + public function testGetPalette() + { + $instance = $this->createTestInstance(); + $prepend = '{test_prepend_legend},testPrependField;'; + $append = '{test_append_legend},testAppendField;'; + $expected = '{config_legend},field,operator,submitOnInput;{visualization_legend},addPlaceholder,addDefaultValue,customLabel,hideLabel,inputGroup;'; + + $this->assertSame($expected, $instance->getPalette('', '')); + $this->assertSame($prepend.$expected, $instance->getPalette($prepend, '')); + $this->assertSame($expected.$append, $instance->getPalette('', $append)); + $this->assertSame($prepend.$expected.$append, $instance->getPalette($prepend, $append)); + } + + public function testGetInitialPalette() + { + $instance = $this->createTestInstance(); + $prepend = '{test_prepend_legend},testPrependField;'; + $append = '{test_append_legend},testAppendField;'; + $expected = '{config_legend},field,operator,initialValueType;'; + + $this->assertSame($expected, $instance->getInitialPalette('', '')); + $this->assertSame($prepend.$expected, $instance->getInitialPalette($prepend, '')); + $this->assertSame($expected.$append, $instance->getInitialPalette('', $append)); + $this->assertSame($prepend.$expected.$append, $instance->getInitialPalette($prepend, $append)); + } + + public function testGetInitialValueTypes() + { + $instance = $this->createTestInstance(); + $typesArray = []; + $this->assertSame([], $instance->getInitialValueTypes($typesArray)); + + $typesArray = [AbstractFilterType::VALUE_TYPE_ARRAY]; + $this->assertSame([], $instance->getInitialValueTypes($typesArray)); + + $typesArray = [AbstractFilterType::VALUE_TYPE_CONTEXTUAL]; + $this->assertSame($typesArray, $instance->getInitialValueTypes($typesArray)); + + $typesArray = [ + AbstractFilterType::VALUE_TYPE_ARRAY, + AbstractFilterType::VALUE_TYPE_CONTEXTUAL, + ]; + $this->assertSame([AbstractFilterType::VALUE_TYPE_CONTEXTUAL], $instance->getInitialValueTypes($typesArray)); + } + + public function testGetOptions() + { + $translator = new Translator('de'); + $instance = $this->createTestInstance(['translator' => $translator]); + + $filterTypeContext = $this->createMock(FilterTypeContext::class); + + $filterConfigElement = $this->mockClassWithProperties(FilterConfigElementModel::class, [ + 'submitOnInput' => true, + ]); + $filterTypeContext->method('getElementConfig')->willReturn($filterConfigElement); + + $filterConfig = $this->createMock(FilterConfigModel::class); + $filterConfig->method('row')->willReturn(['asyncFormSubmit' => true]); + $filterTypeContext->method('getFilterConfig')->willReturn($filterConfig); + + $optionsArray = $instance->getOptions($filterTypeContext); + + $this->assertArrayHasKey('data-submit-on-input', $optionsArray['attr']); + $this->assertArrayHasKey('data-threshold', $optionsArray['attr']); + $this->assertArrayHasKey('data-debounce', $optionsArray['attr']); + + $this->assertSame('1', $optionsArray['attr']['data-submit-on-input']); + $this->assertSame('0', $optionsArray['attr']['data-threshold']); + $this->assertSame('0', $optionsArray['attr']['data-debounce']); + + $config = $this->createMock(FilterConfigModel::class); + $config->method('row')->willReturn(['asyncFormSubmit' => false]); + $filterContext = $this->createMock(FilterTypeContext::class); + $filterContext->method('getFilterConfig')->willReturn($config); + $options = $instance->getOptions($filterContext); + + $this->assertArrayNotHasKey('data-submit-on-input', $options['attr']); + $this->assertArrayNotHasKey('data-threshold', $options['attr']); + $this->assertArrayNotHasKey('data-debounce', $options['attr']); + } + + public function testBuildQuery() + { + $filterContext = $this->createMock(FilterTypeContext::class); + + $queryPartCollection = new FilterQueryPartCollection(); + + $queryPartProcessor = $this->createMock(FilterQueryPartProcessor::class); + + $queryPartProcessor->method('composeQueryPart')->willReturn($this->createMock(FilterQueryPart::class)); + + $instance = $this->createTestInstance(['processor' => $queryPartProcessor, 'collection' => $queryPartCollection]); + $this->assertEmpty($queryPartCollection->getParts()); + + $instance->buildQuery($filterContext); + $this->assertCount(1, $queryPartCollection->getParts()); + } + + public function testBuildForm() + { + $context = $this->createMock(FilterTypeContext::class); + $elementConfig = $this->createMock(FilterConfigElementModel::class); + $elementConfig->method('getElementName')->willReturn('TextType'); + $context->method('getElementConfig')->willReturn($elementConfig); + + $eventDispatcher = $this->createMock(EventDispatcherInterface::class); + $formFactory = $this->createMock(FormFactoryInterface::class); + $formBuilder = new FormBuilder($elementConfig->getElementName(), null, $eventDispatcher, $formFactory); + $context->method('getFormBuilder')->willReturn($formBuilder); + + $this->assertSame(0, $context->getFormBuilder()->count()); + $instance = $this->createTestInstance(); + $instance->buildQuery($context); + + // TODO: why is this not working? +// $this->assertSame(1, $context->getFormBuilder()->count()); + } +} From a70d0cc0a122c7bf299a447da8b6a2f0bffd208e Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 4 May 2021 08:50:00 +0200 Subject: [PATCH 41/58] fixed and added tests for FilterQueryPartCollection --- src/FilterQuery/FilterQueryPartCollection.php | 11 +- .../FilterQueryPartCollectionTest.php | 194 ++++++++++++++++++ 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 tests/FilterQuery/FilterQueryPartCollectionTest.php diff --git a/src/FilterQuery/FilterQueryPartCollection.php b/src/FilterQuery/FilterQueryPartCollection.php index edb4a8bb..d2efd1d7 100644 --- a/src/FilterQuery/FilterQueryPartCollection.php +++ b/src/FilterQuery/FilterQueryPartCollection.php @@ -50,9 +50,17 @@ public function addTargetField(string $field, string $partName, bool $isInitial, $this->targetFields[$field][$partName] = ['initial' => $isInitial, 'overridable' => $overridable]; } - public function removeTargetField(string $field, string $partName): void + public function removeTargetField(string $field = '', string $partName = ''): void { + if ('' === $field) { + return; + } + unset($this->targetFields[$field][$partName]); + + if (empty($this->targetFields[$field])) { + unset($this->targetFields[$field]); + } } public function getTargetFields(): array @@ -63,5 +71,6 @@ public function getTargetFields(): array public function reset(): void { $this->parts = []; + $this->targetFields = []; } } diff --git a/tests/FilterQuery/FilterQueryPartCollectionTest.php b/tests/FilterQuery/FilterQueryPartCollectionTest.php new file mode 100644 index 00000000..f3da7492 --- /dev/null +++ b/tests/FilterQuery/FilterQueryPartCollectionTest.php @@ -0,0 +1,194 @@ +getMockBuilder(FilterQueryPartCollection::class) + ->getMock(); + } else { + $instance = new FilterQueryPartCollection(); + } + + return $instance; + } + + public function mockQueryPart(string $name = 'teaser_70', string $field = 'teaser'): FilterQueryPart + { + $part = $this->createMock(FilterQueryPart::class); + $part->method('getField')->willReturn($field); + $part->method('isInitial')->willReturn(false); + $part->method('isOverridable')->willReturn(false); + $part->method('getName')->willReturn($name); + + return $part; + } + + public function testGetParts() + { + $instance = $this->createTestInstance(); + + $this->assertCount(0, $instance->getParts()); + $this->assertInternalType('array', $instance->getParts()); + $this->assertEmpty($instance->getParts()); + + $instance->addPart($this->mockQueryPart()); + + $this->assertCount(1, $instance->getParts()); + $this->assertInternalType('array', $instance->getParts()); + $this->assertNotEmpty($instance->getParts()); + } + + public function testGetPartByName() + { + $instance = $this->createTestInstance(); + $part = $this->mockQueryPart(); + + $instance->addPart($part); + + $this->assertSame($part, $instance->getPartByName('teaser_70')); + } + + public function testAddPart() + { + $instance = $this->createTestInstance(); + $part = $this->mockQueryPart(); + + $this->assertEmpty($instance->getParts()); + $this->assertEmpty($instance->getTargetFields()); + + $instance->addPart($part); + + $this->assertNotEmpty($instance->getParts()); + $this->assertSame($part, $instance->getParts()['teaser_70']); + $this->assertNotEmpty($instance->getTargetFields()); + } + + public function testRemovePartByName() + { + $instance = $this->createTestInstance(); + $instance->addPart($part = $this->mockQueryPart('teaser_71')); + + $this->assertCount(1, $instance->getParts()); + $this->assertSame($part, $instance->getPartByName('teaser_71')); + + $instance->removePartByName('teaser_71'); + + $this->assertCount(0, $instance->getParts()); + $this->assertNull($instance->getPartByName('teaser_71')); + } + + public function testAddTargetField() + { + $instance = $this->createTestInstance(); + + $this->assertCount(0, $instance->getTargetFields()); + + $instance->addTargetField('teaser', 'teaser_71', false, false); + $this->assertCount(1, $instance->getTargetFields()); + $this->assertCount(1, $instance->getTargetFields()['teaser']); + $this->assertFalse($instance->getTargetFields()['teaser']['teaser_71']['initial']); + $this->assertFalse($instance->getTargetFields()['teaser']['teaser_71']['overridable']); + + $instance->addTargetField('teaser', 'teaser_72', true, false); + $this->assertCount(1, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertTrue($instance->getTargetFields()['teaser']['teaser_72']['initial']); + $this->assertFalse($instance->getTargetFields()['teaser']['teaser_72']['overridable']); + + $instance->addTargetField('content', 'content_73', false, true); + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + $this->assertFalse($instance->getTargetFields()['content']['content_73']['initial']); + $this->assertTrue($instance->getTargetFields()['content']['content_73']['overridable']); + } + + public function testGetTargetFields() + { + $instance = $this->createTestInstance(); + + $instance->addPart($this->mockQueryPart()); + + $this->assertInternalType('array', $instance->getTargetFields()); + $this->assertCount(1, $instance->getTargetFields()); + $this->assertArrayHasKey('teaser', $instance->getTargetFields()); + $this->assertCount(1, $instance->getTargetFields()['teaser']); + $this->assertArrayHasKey('teaser_70', $instance->getTargetFields()['teaser']); + } + + public function testRemoveTargetField() + { + $instance = $this->createTestInstance(); + + $instance->addTargetField('teaser', 'teaser_71', false, false); + $instance->addTargetField('teaser', 'teaser_72', true, false); + $instance->addTargetField('content', 'content_73', false, true); + + $this->assertArrayHasKey('teaser', $instance->getTargetFields()); + $this->assertArrayHasKey('teaser_71', $instance->getTargetFields()['teaser']); + $this->assertArrayHasKey('teaser_72', $instance->getTargetFields()['teaser']); + $this->assertArrayHasKey('content', $instance->getTargetFields()); + $this->assertArrayHasKey('content_73', $instance->getTargetFields()['content']); + + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + + $instance->removeTargetField(); + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + + $instance->removeTargetField('', 'teaser_71'); + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + + $instance->removeTargetField('content', 'teaser_71'); + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(2, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + + $instance->removeTargetField('teaser', 'teaser_71'); + $this->assertCount(2, $instance->getTargetFields()); + $this->assertCount(1, $instance->getTargetFields()['teaser']); + $this->assertCount(1, $instance->getTargetFields()['content']); + + $instance->removeTargetField('content', 'content_73'); + $this->assertCount(1, $instance->getTargetFields()); + $this->assertCount(1, $instance->getTargetFields()['teaser']); + $this->assertArrayNotHasKey('content', $instance->getTargetFields()); + } + + public function testReset() + { + $instance = $this->createTestInstance(); + + $instance->addPart($this->mockQueryPart()); + $this->assertCount(1, $instance->getParts()); + $this->assertCount(1, $instance->getTargetFields()); + + $instance->reset(); + $this->assertEmpty($instance->getParts()); + $this->assertEmpty($instance->getTargetFields()); + } +} From 78c5d23637ce36f4b4b81942c48b27cfcf3d4b1d Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 4 May 2021 11:10:20 +0200 Subject: [PATCH 42/58] added more options to choiceType --- src/Choice/FieldOptionsChoice.php | 89 +++++++++++++++++-------------- src/Type/Concrete/ChoiceType.php | 2 +- 2 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/Choice/FieldOptionsChoice.php b/src/Choice/FieldOptionsChoice.php index a0e882e0..f9c1ae4f 100644 --- a/src/Choice/FieldOptionsChoice.php +++ b/src/Choice/FieldOptionsChoice.php @@ -118,7 +118,7 @@ protected function getCustomOptions(FilterConfigElementModel $element, array $fi $options = StringUtil::deserialize($element->options, true); - return $options; + return $this->adjustOptionLabels($element, $filter, $options); } /** @@ -255,45 +255,7 @@ protected function getWidgetOptions(FilterConfigElementModel $element, array $fi } } - if (!empty($options) && true === (bool) $element->adjustOptionLabels && !empty($element->optionLabelPattern)) { - if (null !== ($filterQueryBuilder = System::getContainer()->get('huh.filter.manager')->getQueryBuilder($filter['id'], [$element->id]))) { - $filterQueryBuilder->select([$filter['dataContainer'].'.'.$element->field, $filter['dataContainer'].'.*']); - $filterQueryBuilder->orderBy($element->field); - $rows = $filterQueryBuilder->execute()->fetchAll(); - - $data = []; - - foreach ($rows as $row) { - $currentValue = $row[$element->field]; - - if (isset($data[$currentValue])) { - ++$data[$currentValue]['count']; - - continue; - } - - $data[$currentValue] = ['data' => $row, 'count' => 1]; - } - - foreach ($options as $key => &$option) { - if (!isset($option['label']) || !isset($rows[$option['value']])) { - continue; - } - - $params = $data[$option['value']]; - $params['label'] = $option['label']; - - foreach ($params as $key => $value) { - unset($params[$key]); - $params['%'.$key.'%'] = $value; - } - - $option['label'] = System::getContainer()->get('translator')->trans($element->optionLabelPattern, $params); - } - } - } - - return $options; + return $this->adjustOptionLabels($element, $filter, $options); } /** @@ -367,4 +329,51 @@ protected function getGroupChoicesValue(array $choices, FilterConfigElementModel return implode(',', $choices); } + + private function adjustOptionLabels(FilterConfigElementModel $element, array $filter, array $options): array + { + if (!empty($options) && true === (bool) $element->adjustOptionLabels && !empty($element->optionLabelPattern)) { + if (null !== ($filterQueryBuilder = System::getContainer()->get('huh.filter.manager')->getQueryBuilder($filter['id'], [$element->id]))) { + $filterQueryBuilder->select([$filter['dataContainer'].'.'.$element->field, $filter['dataContainer'].'.*']); + $filterQueryBuilder->orderBy($element->field); + $rows = $filterQueryBuilder->execute()->fetchAll(); + + $data = []; + + foreach ($rows as $row) { + $currentValue = $row[$element->field]; + + if (isset($data[$currentValue])) { + ++$data[$currentValue]['count']; + + continue; + } + + $data[$currentValue] = ['data' => $row, 'count' => 1]; + } + + foreach ($options as &$option) { + if (!isset($option['label']) || !isset($rows[$option['value']])) { + continue; + } + + $params = $data[$option['value']]; + $params['label'] = $option['label']; + + foreach ($params as $key => $value) { + unset($params[$key]); + $params['%'.$key.'%'] = $value; + } + + if (!$params['%count%']) { + $params['%count%'] = 0; + } + + $option['label'] = System::getContainer()->get('translator')->trans($element->optionLabelPattern, $params); + } + } + } + + return $options; + } } diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 54054c95..2a036f3a 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -63,7 +63,7 @@ public function buildForm($filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator,submitOnChange,expanded,multiple;{visualization_legend},addPlaceholder,customLabel,hideLabel;{expert_legend},cssClass;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,customOptions,reviseOptions,dynamicOptions,sortOptionValues,adjustOptionLabels,submitOnChange,expanded,multiple,addGroupChoiceField,doNotCacheOptions;{visualization_legend},addPlaceholder,customLabel,hideLabel;'.$appendPalette; } public function getOperators(): array From 0b7754f5ee9a49a8c614690e4aed95f798d98ba7 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 4 May 2021 14:51:36 +0200 Subject: [PATCH 43/58] refactored initialValueChoices --- .../FilterConfigElementContainer.php | 42 +++++++++++++ src/FilterQuery/FilterQueryPart.php | 7 ++- .../contao/dca/tl_filter_config_element.php | 63 ++++++++++--------- src/Type/Concrete/ChoiceType.php | 22 ++++++- 4 files changed, 99 insertions(+), 35 deletions(-) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 06ec99ae..372cc38b 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -11,16 +11,19 @@ use Contao\DataContainer; use Contao\DC_Table; use Contao\System; +use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; use HeimrichHannot\FilterBundle\Choice\TypeChoice; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\Concrete\ButtonType; use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; +use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; use HeimrichHannot\FilterBundle\Type\PlaceholderFilterTypeInterface; use HeimrichHannot\UtilsBundle\Choice\MessageChoice; use HeimrichHannot\UtilsBundle\Container\ContainerUtil; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; +use HeimrichHannot\UtilsBundle\Model\ModelUtil; class FilterConfigElementContainer { @@ -40,16 +43,28 @@ class FilterConfigElementContainer * @var ContainerUtil */ protected $container; + /** * @var MessageChoice */ protected $messageChoice; + /** + * @var ModelUtil + */ + protected $modelUtil; + /** + * @var FieldOptionsChoice + */ + protected $fieldOptionsChoice; + public function __construct( array $bundleConfig, TypeChoice $typeChoice, FilterTypeCollection $typeCollection, + FieldOptionsChoice $fieldOptionsChoice, ContainerUtil $container, + ModelUtil $modelUtil, MessageChoice $messageChoice ) { $this->bundleConfig = $bundleConfig; @@ -57,6 +72,8 @@ public function __construct( $this->typeCollection = $typeCollection; $this->container = $container; $this->messageChoice = $messageChoice; + $this->modelUtil = $modelUtil; + $this->fieldOptionsChoice = $fieldOptionsChoice; } public function onLoadCallback(DataContainer $dc): void @@ -126,6 +143,31 @@ public function onInitialValueTypeCallback(DC_Table $dc): array return $class::VALUE_TYPES; } + public function onInitialValueCallback(DC_Table $dc) + { + /** @var FilterConfigElementModel $element */ + if (null === ($element = $this->modelUtil->findModelInstanceByPk($dc->table, $dc->id))) { + return null; + } + + if ($this->typeCollection->getType($element->type) instanceof InitialFilterTypeInterface) { + $context = new FilterTypeContext(); + $context->setElementConfig($element); + $context->setFilterConfig($element->getRelated('pid')); + + if (!method_exists($this->typeCollection->getType($element->type), 'getInitialValueChoices')) { + return null; + } + + return $this->typeCollection->getType($element->type)->getInitialValueChoices($context); + } + + return $this->fieldOptionsChoice->getCachedChoices([ + 'element' => $element, + 'filter' => $element->getRelated('pid')->row(), + ]); + } + public function onOperatorOptionsCallback(DataContainer $dc) { if (null === $this->typeCollection->getType($dc->activeRecord->type)) { diff --git a/src/FilterQuery/FilterQueryPart.php b/src/FilterQuery/FilterQueryPart.php index b99e8b92..7bda1c63 100644 --- a/src/FilterQuery/FilterQueryPart.php +++ b/src/FilterQuery/FilterQueryPart.php @@ -8,6 +8,7 @@ namespace HeimrichHannot\FilterBundle\FilterQuery; +use Contao\StringUtil; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; class FilterQueryPart @@ -84,10 +85,10 @@ public function __construct(FilterTypeContext $filterTypeContext) if ($elementConfig->isInitial) { $this->initial = $elementConfig->isInitial; - $this->initialValue = $elementConfig->initialValue ?: $elementConfig->initialValueArray; + $this->initialValue = $elementConfig->initialValue ?: array_column(StringUtil::deserialize($elementConfig->initialValueArray, true), 'value'); $this->initialValueType = $elementConfig->initialValueType; - $this->value = $this->initialValue; - $this->valueType = $this->initialValueType; + $this->value = $elementConfig->initialValue ?: $this->initialValue; + $this->valueType = $elementConfig->initialValueType; $this->overridable = $elementConfig->isInitialOverridable; } else { $this->value = $filterTypeContext->getValue(); diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index f83880fd..7213e44e 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -257,7 +257,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['chosen' => true, 'includeBlankOption' => true, 'tl_class' => 'w50', 'submitOnChange' => true], @@ -268,7 +268,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'checkboxWizard', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['chosen' => true, 'includeBlankOption' => true, 'multiple' => true, 'mandatory' => true], @@ -279,7 +279,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['chosen' => true, 'includeBlankOption' => true], @@ -310,7 +310,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['optionLabelPattern'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { + 'options_callback' => function (DataContainer $dc) { return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.option_label'); }, 'eval' => ['chosen' => true, 'mandatory' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], @@ -380,7 +380,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['placeholder'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { + 'options_callback' => function (DataContainer $dc) { return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.placeholder'); }, 'eval' => ['chosen' => true, 'mandatory' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], @@ -404,7 +404,7 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['label'], 'exclude' => true, 'inputType' => 'select', - 'options_callback' => function (\DataContainer $dc) { + 'options_callback' => function (DataContainer $dc) { return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.label'); }, 'eval' => ['chosen' => true, 'mandatory' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], @@ -439,7 +439,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { if (null === ($model = \Contao\System::getContainer()->get('huh.utils.model')->findModelInstanceByPk($dc->table, $dc->id))) { return []; } @@ -470,7 +470,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { if (null === ($model = \Contao\System::getContainer()->get('huh.utils.model')->findModelInstanceByPk($dc->table, $dc->id))) { return []; } @@ -636,7 +636,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => 'EUR', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return Symfony\Component\Intl\Intl::getCurrencyBundle()->getCurrencyNames(); }, 'eval' => ['tl_class' => 'clr w50 wizard', 'chosen' => 'true', 'maxlength' => 3], @@ -709,7 +709,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => 'EUR', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return Symfony\Component\Intl\Intl::getRegionBundle()->getCountryNames(); }, 'eval' => ['tl_class' => 'clr w50 wizard', 'chosen' => 'true', 'multiple' => true, 'mandatory' => true], @@ -727,7 +727,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => 'EUR', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return Symfony\Component\Intl\Intl::getLanguageBundle()->getLanguageNames(); }, 'eval' => ['tl_class' => 'clr w50 wizard', 'chosen' => 'true', 'multiple' => true, 'mandatory' => true], @@ -745,7 +745,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => 'EUR', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return Symfony\Component\Intl\Intl::getLocaleBundle()->getLocaleNames(); }, 'eval' => ['tl_class' => 'clr w50 wizard', 'chosen' => 'true', 'multiple' => true, 'mandatory' => true], @@ -794,8 +794,9 @@ 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['initialValue_value'], 'exclude' => true, 'search' => true, - 'inputType' => 'text', - 'eval' => ['tl_class' => 'w50', 'mandatory' => true, 'groupStyle' => 'width: 200px'], + 'inputType' => 'select', + 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onInitialValueCallback'], + 'eval' => ['tl_class' => 'w50', 'mandatory' => true, 'groupStyle' => 'width: 49%'], ], ], ], @@ -872,7 +873,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['chosen' => true, 'includeBlankOption' => true, 'tl_class' => 'w50', 'mandatory' => true], @@ -883,7 +884,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['chosen' => true, 'includeBlankOption' => true, 'tl_class' => 'w50', 'mandatory' => true], @@ -951,7 +952,7 @@ 'exclude' => true, 'search' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getSortClasses($dc); }, 'eval' => [ @@ -967,7 +968,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => [ @@ -983,7 +984,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getSortDirections($dc); }, 'eval' => [ @@ -999,7 +1000,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.sort.text'); }, 'eval' => [ @@ -1117,7 +1118,7 @@ 'exclude' => true, 'inputType' => 'select', 'default' => 'huh.filter.option_count.default', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.utils.choice.message')->getCachedChoices('huh.filter.option_count'); }, 'eval' => ['chosen' => true, 'mandatory' => true, 'maxlength' => 128, 'includeBlankOption' => true, 'tl_class' => 'w50'], @@ -1182,7 +1183,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['tl_class' => 'w50', 'includeBlankOption' => true, 'chosen' => true, 'mandatory' => true], @@ -1193,7 +1194,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['tl_class' => 'w50', 'includeBlankOption' => true, 'chosen' => true, 'mandatory' => true], @@ -1204,7 +1205,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getFields($dc); }, 'eval' => ['tl_class' => 'w50', 'includeBlankOption' => true, 'chosen' => true, 'mandatory' => true], @@ -1223,7 +1224,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc); }, 'eval' => ['chosen' => true, 'tl_class' => 'w50 clr', 'includeBlankOption' => true], @@ -1234,7 +1235,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc); }, 'eval' => ['chosen' => true, 'tl_class' => 'w50', 'includeBlankOption' => true], @@ -1245,7 +1246,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc); }, 'eval' => ['chosen' => true, 'tl_class' => 'w50', 'includeBlankOption' => true], @@ -1256,7 +1257,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc); }, 'eval' => ['chosen' => true, 'tl_class' => 'w50', 'includeBlankOption' => true], @@ -1267,7 +1268,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc, [ 'types' => [ 'checkbox', @@ -1282,7 +1283,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc, [ 'types' => [ 'hidden', @@ -1297,7 +1298,7 @@ 'exclude' => true, 'filter' => true, 'inputType' => 'select', - 'options_callback' => function (\Contao\DataContainer $dc) { + 'options_callback' => function (Contao\DataContainer $dc) { return \Contao\System::getContainer()->get('huh.filter.util.filter_config_element')->getElements($dc, [ 'types' => [ 'radius_choice', diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 2a036f3a..b20e1b0b 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -14,12 +14,13 @@ use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; use Symfony\Contracts\Translation\TranslatorInterface; -class ChoiceType extends AbstractFilterType +class ChoiceType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'choice_type'; @@ -141,4 +142,23 @@ public function collectChoices(FilterTypeContext $filterTypeContext): array 'filter' => $filterTypeContext->getFilterConfig()->row(), ]); } + + public function getInitialPalette(string $prependPalette, string $appendPalette): string + { + return $prependPalette.'{config_legend},field,operator,initialValueType;'.$appendPalette; + } + + public function getInitialValueChoices(FilterTypeContext $filterTypeContext): array + { + return $this->collectChoices($filterTypeContext); + } + + public function getInitialValueTypes(array $types): array + { + $remove = [ + AbstractFilterType::VALUE_TYPE_SCALAR, + ]; + + return array_values(array_diff($types, $remove)); + } } From 580b28f5d1437a1a0b14397ab2b963301ca69c45 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 4 May 2021 16:02:47 +0200 Subject: [PATCH 44/58] fixed corrupted options array for filterTypes --- src/DataContainer/FilterConfigElementContainer.php | 4 ++++ src/Type/Concrete/ChoiceType.php | 1 + src/Type/Concrete/DateTimeType.php | 1 + 3 files changed, 6 insertions(+) diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 372cc38b..19064f6e 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -119,6 +119,10 @@ public function onTypeOptionsCallback(DataContainer $dc): array } } + foreach ($options as $key => $option) { + $options[$key] = array_values($options[$key]); + } + return $options; } diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index b20e1b0b..0e1256a0 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -23,6 +23,7 @@ class ChoiceType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'choice_type'; + const GROUP = 'choice'; /** * @var FieldOptionsChoice diff --git a/src/Type/Concrete/DateTimeType.php b/src/Type/Concrete/DateTimeType.php index a4b9d8be..824e1366 100644 --- a/src/Type/Concrete/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -23,6 +23,7 @@ class DateTimeType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'date_time_type'; + const GROUP = 'date'; const WIDGET_TYPE_CHOICE = 'choice'; const WIDGET_TYPE_SINGLE_TEXT = 'single_text'; From 89e52f1a2a5521ed5ecb2279b3c69d69d4ec1e6d Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 25 Jun 2021 15:32:18 +0200 Subject: [PATCH 45/58] added translation and deprecated all old filtertypes --- src/Config/FilterConfig.php | 10 ++-- .../FilterConfigElementContainer.php | 11 ++-- src/DependencyInjection/Configuration.php | 4 -- src/Filter/AbstractType.php | 3 ++ src/Filter/Type/AutoItemType.php | 3 ++ src/Filter/Type/ButtonType.php | 2 +- src/Filter/Type/CheckboxType.php | 3 ++ src/Filter/Type/ChoiceType.php | 2 +- src/Filter/Type/ColorType.php | 3 ++ src/Filter/Type/CountryType.php | 3 ++ src/Filter/Type/CurrentMemberType.php | 3 ++ src/Filter/Type/DateChoiceType.php | 3 ++ src/Filter/Type/DateRangeType.php | 3 ++ src/Filter/Type/DateTimeType.php | 2 +- src/Filter/Type/DateType.php | 3 ++ src/Filter/Type/EmailType.php | 3 ++ src/Filter/Type/ExternalEntityType.php | 3 ++ src/Filter/Type/HiddenType.php | 3 ++ src/Filter/Type/IntegerType.php | 3 ++ src/Filter/Type/LanguageType.php | 3 ++ src/Filter/Type/LocaleType.php | 3 ++ src/Filter/Type/MoneyType.php | 3 ++ src/Filter/Type/MultipleRangeType.php | 3 ++ src/Filter/Type/NumberType.php | 3 ++ src/Filter/Type/ParentType.php | 3 ++ src/Filter/Type/PasswordType.php | 3 ++ src/Filter/Type/PercentType.php | 3 ++ src/Filter/Type/ProximitySearchType.php | 3 ++ src/Filter/Type/PublishedType.php | 3 ++ src/Filter/Type/RadioType.php | 3 ++ src/Filter/Type/RadiusChoiceType.php | 3 ++ src/Filter/Type/RangeType.php | 3 ++ src/Filter/Type/ResetType.php | 2 +- src/Filter/Type/SearchType.php | 3 ++ src/Filter/Type/SkipParentsType.php | 3 ++ src/Filter/Type/SortType.php | 3 ++ src/Filter/Type/SqlType.php | 3 ++ src/Filter/Type/SubmitType.php | 2 +- src/Filter/Type/TelType.php | 3 ++ src/Filter/Type/TextConcatType.php | 3 ++ src/Filter/Type/TextType.php | 2 +- src/Filter/Type/TextareaType.php | 3 ++ src/Filter/Type/TimeType.php | 3 ++ src/Filter/Type/UrlType.php | 3 ++ src/Filter/Type/YearType.php | 3 ++ src/Module/ModuleFilter.php | 6 +++ src/Resources/config/config.yml | 1 - .../contao/dca/tl_filter_config_element.php | 4 +- .../languages/de/tl_filter_config_element.php | 1 + src/Resources/translations/messages.de.yml | 2 +- src/Type/Concrete/ChoiceType.php | 4 +- .../FilterQueryPartProcessorTest.php | 53 +++++++++++++++++++ 52 files changed, 188 insertions(+), 28 deletions(-) create mode 100644 tests/FilterQuery/FilterQueryPartProcessorTest.php diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 043ad6f6..7996dcc7 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -823,7 +823,7 @@ protected function mapFormsToData() private function prepareFilterQueryParts(FilterQueryPartCollection $filterQueryPartCollection) { foreach ($filterQueryPartCollection->getTargetFields() as $targetField) { - if (1 >= \count($targetField)) { + if (1 >= count($targetField)) { continue; } @@ -842,11 +842,11 @@ private function prepareFilterQueryParts(FilterQueryPartCollection $filterQueryP $targetKey = array_search($key, array_keys($targetField), true); if (false !== $targetKey) { - $leftover = \array_slice($targetField, $targetKey + 1, null, true); + $targetFieldsSlice = \array_slice($targetField, $targetKey + 1, null, true); - if (!empty($leftover)) { - foreach ($leftover as $leftoverElement) { - if (!$leftoverElement['initial'] && null !== ($element = $this->filterQueryPartCollection->getPartByName($key))) { + if (!empty($targetFieldsSlice)) { + foreach ($targetFieldsSlice as $sliceElement) { + if (!$sliceElement['initial'] && null !== ($element = $this->filterQueryPartCollection->getPartByName($key))) { $element->setDisabled(true); } } diff --git a/src/DataContainer/FilterConfigElementContainer.php b/src/DataContainer/FilterConfigElementContainer.php index 19064f6e..99c74c8b 100644 --- a/src/DataContainer/FilterConfigElementContainer.php +++ b/src/DataContainer/FilterConfigElementContainer.php @@ -109,12 +109,11 @@ public function onTypeOptionsCallback(DataContainer $dc): array // separate deprecated types $options['deprecated'] = []; - foreach ($this->bundleConfig['filter']['deprecated_types'] as $deprecated) { - foreach ($options as $key => $option) { - if (\in_array($deprecated, $option)) { - $helperKey = array_search($deprecated, $option); - unset($options[$key][$helperKey]); - $options['deprecated'][] = $deprecated; + foreach ($options as $optionKey => $option) { + foreach ($option as $typeKey => $type) { + if (!$this->typeCollection->hasType($type)) { + unset($options[$optionKey][$typeKey]); + $options['deprecated'][] = $type; } } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index cbd7a87b..5b4873f7 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -39,10 +39,6 @@ public function getConfigTreeBuilder() ->arrayNode('filter') ->addDefaultsIfNotSet() ->children() - ->arrayNode('deprecated_types') - ->scalarPrototype()->end() - ->info('Add deprecated types here.') - ->end() ->arrayNode('types') ->arrayPrototype() ->children() diff --git a/src/Filter/AbstractType.php b/src/Filter/AbstractType.php index 53e1649a..71842d8d 100644 --- a/src/Filter/AbstractType.php +++ b/src/Filter/AbstractType.php @@ -18,6 +18,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Translation\TranslatorInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0, use HeimrichHannot\FilterBundle\Type\AbstractFilterType + */ abstract class AbstractType { const VALUE_TYPE_SCALAR = 'scalar'; diff --git a/src/Filter/Type/AutoItemType.php b/src/Filter/Type/AutoItemType.php index bbabad94..0aec51d1 100644 --- a/src/Filter/Type/AutoItemType.php +++ b/src/Filter/Type/AutoItemType.php @@ -15,6 +15,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class AutoItemType extends AbstractType { const TYPE = 'auto_item'; diff --git a/src/Filter/Type/ButtonType.php b/src/Filter/Type/ButtonType.php index 21e313eb..f210e1ab 100644 --- a/src/Filter/Type/ButtonType.php +++ b/src/Filter/Type/ButtonType.php @@ -14,7 +14,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType */ class ButtonType extends AbstractType { diff --git a/src/Filter/Type/CheckboxType.php b/src/Filter/Type/CheckboxType.php index a42084b0..d6f3664a 100644 --- a/src/Filter/Type/CheckboxType.php +++ b/src/Filter/Type/CheckboxType.php @@ -14,6 +14,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class CheckboxType extends AbstractType { const TYPE = 'checkbox'; diff --git a/src/Filter/Type/ChoiceType.php b/src/Filter/Type/ChoiceType.php index bf7dec79..14110c54 100644 --- a/src/Filter/Type/ChoiceType.php +++ b/src/Filter/Type/ChoiceType.php @@ -17,7 +17,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ChoiceType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\ChoiceType */ class ChoiceType extends AbstractType { diff --git a/src/Filter/Type/ColorType.php b/src/Filter/Type/ColorType.php index 9959d522..277f96c2 100644 --- a/src/Filter/Type/ColorType.php +++ b/src/Filter/Type/ColorType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class ColorType extends TextType { const TYPE = 'color'; diff --git a/src/Filter/Type/CountryType.php b/src/Filter/Type/CountryType.php index 618b86ab..6cc9a28f 100644 --- a/src/Filter/Type/CountryType.php +++ b/src/Filter/Type/CountryType.php @@ -12,6 +12,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class CountryType extends ChoiceType { const TYPE = 'country'; diff --git a/src/Filter/Type/CurrentMemberType.php b/src/Filter/Type/CurrentMemberType.php index 65831b38..d59967a5 100644 --- a/src/Filter/Type/CurrentMemberType.php +++ b/src/Filter/Type/CurrentMemberType.php @@ -14,6 +14,9 @@ use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class CurrentMemberType extends AbstractType { const TYPE = 'current_member'; diff --git a/src/Filter/Type/DateChoiceType.php b/src/Filter/Type/DateChoiceType.php index 8a45c979..e173b4f3 100644 --- a/src/Filter/Type/DateChoiceType.php +++ b/src/Filter/Type/DateChoiceType.php @@ -19,6 +19,9 @@ use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class DateChoiceType extends ChoiceType { const TYPE = 'date_choice'; diff --git a/src/Filter/Type/DateRangeType.php b/src/Filter/Type/DateRangeType.php index ca022a09..cad66feb 100644 --- a/src/Filter/Type/DateRangeType.php +++ b/src/Filter/Type/DateRangeType.php @@ -16,6 +16,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class DateRangeType extends AbstractType { const TYPE = 'date_range'; diff --git a/src/Filter/Type/DateTimeType.php b/src/Filter/Type/DateTimeType.php index 80e9d9e7..ddd812ba 100644 --- a/src/Filter/Type/DateTimeType.php +++ b/src/Filter/Type/DateTimeType.php @@ -18,7 +18,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\DateTimeType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\DateTimeType */ class DateTimeType extends AbstractType { diff --git a/src/Filter/Type/DateType.php b/src/Filter/Type/DateType.php index 6ae166db..861f5d7b 100644 --- a/src/Filter/Type/DateType.php +++ b/src/Filter/Type/DateType.php @@ -17,6 +17,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class DateType extends AbstractType { const TYPE = 'date'; diff --git a/src/Filter/Type/EmailType.php b/src/Filter/Type/EmailType.php index 1dd12f1c..24c86b57 100644 --- a/src/Filter/Type/EmailType.php +++ b/src/Filter/Type/EmailType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class EmailType extends TextType { const TYPE = 'email'; diff --git a/src/Filter/Type/ExternalEntityType.php b/src/Filter/Type/ExternalEntityType.php index 0b6b238b..b03c0101 100644 --- a/src/Filter/Type/ExternalEntityType.php +++ b/src/Filter/Type/ExternalEntityType.php @@ -16,6 +16,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class ExternalEntityType extends AbstractType { const TYPE = 'external_entity'; diff --git a/src/Filter/Type/HiddenType.php b/src/Filter/Type/HiddenType.php index e5210118..fec13708 100644 --- a/src/Filter/Type/HiddenType.php +++ b/src/Filter/Type/HiddenType.php @@ -14,6 +14,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class HiddenType extends AbstractType { const TYPE = 'hidden'; diff --git a/src/Filter/Type/IntegerType.php b/src/Filter/Type/IntegerType.php index be505ac0..79244738 100644 --- a/src/Filter/Type/IntegerType.php +++ b/src/Filter/Type/IntegerType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class IntegerType extends TextType { const TYPE = 'integer'; diff --git a/src/Filter/Type/LanguageType.php b/src/Filter/Type/LanguageType.php index 92ef5d20..2fa94dfe 100644 --- a/src/Filter/Type/LanguageType.php +++ b/src/Filter/Type/LanguageType.php @@ -12,6 +12,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class LanguageType extends ChoiceType { const TYPE = 'language'; diff --git a/src/Filter/Type/LocaleType.php b/src/Filter/Type/LocaleType.php index 7df6ab5a..7bae1973 100644 --- a/src/Filter/Type/LocaleType.php +++ b/src/Filter/Type/LocaleType.php @@ -12,6 +12,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class LocaleType extends ChoiceType { const TYPE = 'locale'; diff --git a/src/Filter/Type/MoneyType.php b/src/Filter/Type/MoneyType.php index cb673ee1..7de921b4 100644 --- a/src/Filter/Type/MoneyType.php +++ b/src/Filter/Type/MoneyType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class MoneyType extends TextType { const TYPE = 'money'; diff --git a/src/Filter/Type/MultipleRangeType.php b/src/Filter/Type/MultipleRangeType.php index 2c424dfb..3e3484dc 100644 --- a/src/Filter/Type/MultipleRangeType.php +++ b/src/Filter/Type/MultipleRangeType.php @@ -16,6 +16,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class MultipleRangeType extends AbstractType { const TYPE = 'multiple_range'; diff --git a/src/Filter/Type/NumberType.php b/src/Filter/Type/NumberType.php index b66a1945..071dbb71 100644 --- a/src/Filter/Type/NumberType.php +++ b/src/Filter/Type/NumberType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class NumberType extends TextType { const TYPE = 'number'; diff --git a/src/Filter/Type/ParentType.php b/src/Filter/Type/ParentType.php index fda07eea..6509709a 100644 --- a/src/Filter/Type/ParentType.php +++ b/src/Filter/Type/ParentType.php @@ -12,6 +12,9 @@ use Contao\System; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class ParentType extends ChoiceType { const TYPE = 'filterConfig'; diff --git a/src/Filter/Type/PasswordType.php b/src/Filter/Type/PasswordType.php index 64028c77..8c7abb2e 100644 --- a/src/Filter/Type/PasswordType.php +++ b/src/Filter/Type/PasswordType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class PasswordType extends TextType { const TYPE = 'password'; diff --git a/src/Filter/Type/PercentType.php b/src/Filter/Type/PercentType.php index 4ba1069a..a8cbf8e4 100644 --- a/src/Filter/Type/PercentType.php +++ b/src/Filter/Type/PercentType.php @@ -12,6 +12,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class PercentType extends TextType { const TYPE = 'percent'; diff --git a/src/Filter/Type/ProximitySearchType.php b/src/Filter/Type/ProximitySearchType.php index db2206ac..4278d10a 100644 --- a/src/Filter/Type/ProximitySearchType.php +++ b/src/Filter/Type/ProximitySearchType.php @@ -16,6 +16,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class ProximitySearchType extends AbstractType { const TYPE = 'proximity_search'; diff --git a/src/Filter/Type/PublishedType.php b/src/Filter/Type/PublishedType.php index 5782e2ac..49e6cf21 100644 --- a/src/Filter/Type/PublishedType.php +++ b/src/Filter/Type/PublishedType.php @@ -15,6 +15,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class PublishedType extends AbstractType { const TYPE = 'visible'; diff --git a/src/Filter/Type/RadioType.php b/src/Filter/Type/RadioType.php index 7eac198c..583d31e7 100644 --- a/src/Filter/Type/RadioType.php +++ b/src/Filter/Type/RadioType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class RadioType extends CheckboxType { /** diff --git a/src/Filter/Type/RadiusChoiceType.php b/src/Filter/Type/RadiusChoiceType.php index fc058d69..a784b9a3 100644 --- a/src/Filter/Type/RadiusChoiceType.php +++ b/src/Filter/Type/RadiusChoiceType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class RadiusChoiceType extends ChoiceType { const TYPE = 'radius_choice'; diff --git a/src/Filter/Type/RangeType.php b/src/Filter/Type/RangeType.php index 9e62834c..1c02725e 100644 --- a/src/Filter/Type/RangeType.php +++ b/src/Filter/Type/RangeType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class RangeType extends TextType { const TYPE = 'range'; diff --git a/src/Filter/Type/ResetType.php b/src/Filter/Type/ResetType.php index 52ac1260..8f6036cc 100644 --- a/src/Filter/Type/ResetType.php +++ b/src/Filter/Type/ResetType.php @@ -14,7 +14,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType */ class ResetType extends AbstractType { diff --git a/src/Filter/Type/SearchType.php b/src/Filter/Type/SearchType.php index a9680744..8edc9eb1 100644 --- a/src/Filter/Type/SearchType.php +++ b/src/Filter/Type/SearchType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class SearchType extends TextType { /** diff --git a/src/Filter/Type/SkipParentsType.php b/src/Filter/Type/SkipParentsType.php index 4a243d5f..b2b88236 100644 --- a/src/Filter/Type/SkipParentsType.php +++ b/src/Filter/Type/SkipParentsType.php @@ -15,6 +15,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class SkipParentsType extends AbstractType { const TYPE = 'skip_parents'; diff --git a/src/Filter/Type/SortType.php b/src/Filter/Type/SortType.php index d240c2e3..f88226bb 100644 --- a/src/Filter/Type/SortType.php +++ b/src/Filter/Type/SortType.php @@ -14,6 +14,9 @@ use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class SortType extends ChoiceType { const TYPE = 'sort'; diff --git a/src/Filter/Type/SqlType.php b/src/Filter/Type/SqlType.php index 894cde68..b2385af6 100644 --- a/src/Filter/Type/SqlType.php +++ b/src/Filter/Type/SqlType.php @@ -15,6 +15,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class SqlType extends AbstractType { const TYPE = 'sql'; diff --git a/src/Filter/Type/SubmitType.php b/src/Filter/Type/SubmitType.php index 9dc39552..b2306218 100644 --- a/src/Filter/Type/SubmitType.php +++ b/src/Filter/Type/SubmitType.php @@ -14,7 +14,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\ButtonType */ class SubmitType extends AbstractType { diff --git a/src/Filter/Type/TelType.php b/src/Filter/Type/TelType.php index d2451acf..420368b5 100644 --- a/src/Filter/Type/TelType.php +++ b/src/Filter/Type/TelType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class TelType extends TextType { /** diff --git a/src/Filter/Type/TextConcatType.php b/src/Filter/Type/TextConcatType.php index b40c4257..c6d8a182 100644 --- a/src/Filter/Type/TextConcatType.php +++ b/src/Filter/Type/TextConcatType.php @@ -18,6 +18,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class TextConcatType extends AbstractType { const TYPE = 'text_concat'; diff --git a/src/Filter/Type/TextType.php b/src/Filter/Type/TextType.php index 20d53c2b..97bdaa2f 100644 --- a/src/Filter/Type/TextType.php +++ b/src/Filter/Type/TextType.php @@ -15,7 +15,7 @@ use Symfony\Component\Form\FormBuilderInterface; /** - * @deprecated since 1.11, use HeimrichHannot\FilterBundle\FilterType\Type\TextType + * @deprecated since 1.12, use HeimrichHannot\FilterBundle\FilterType\Type\TextType */ class TextType extends AbstractType { diff --git a/src/Filter/Type/TextareaType.php b/src/Filter/Type/TextareaType.php index 3bf40e73..24c47775 100644 --- a/src/Filter/Type/TextareaType.php +++ b/src/Filter/Type/TextareaType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class TextareaType extends TextType { /** diff --git a/src/Filter/Type/TimeType.php b/src/Filter/Type/TimeType.php index 93bf4954..438a4a99 100644 --- a/src/Filter/Type/TimeType.php +++ b/src/Filter/Type/TimeType.php @@ -17,6 +17,9 @@ use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class TimeType extends AbstractType { /** diff --git a/src/Filter/Type/UrlType.php b/src/Filter/Type/UrlType.php index 9285d6df..1ef51fab 100644 --- a/src/Filter/Type/UrlType.php +++ b/src/Filter/Type/UrlType.php @@ -11,6 +11,9 @@ use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class UrlType extends TextType { /** diff --git a/src/Filter/Type/YearType.php b/src/Filter/Type/YearType.php index 6867be9d..37c79b6a 100644 --- a/src/Filter/Type/YearType.php +++ b/src/Filter/Type/YearType.php @@ -19,6 +19,9 @@ use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\FormBuilderInterface; +/** + * @deprecated since 1.12 and will be removed in version 2.0 + */ class YearType extends ChoiceType { const TYPE = 'year'; diff --git a/src/Module/ModuleFilter.php b/src/Module/ModuleFilter.php index 6de72a5c..2a627422 100644 --- a/src/Module/ModuleFilter.php +++ b/src/Module/ModuleFilter.php @@ -50,6 +50,10 @@ public function generate() return ''; } + if (null === $this->config->getElements()) { + return ''; + } + $this->config->handleRequest(); return parent::generate(); @@ -66,6 +70,8 @@ protected function compile() $this->config->buildForm($this->config->getData()); } + + $form = $this->config->getBuilder()->getForm(); /** diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index 2a1681bf..2dfea40e 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -1,6 +1,5 @@ huh: filter: - deprecated_types: ['button', 'reset', 'submit', 'text', 'choice', 'date_time'] types: - { name: text, class: HeimrichHannot\FilterBundle\Filter\Type\TextType, type: text } - { name: text_concat, class: HeimrichHannot\FilterBundle\Filter\Type\TextConcatType, type: text } diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index 7213e44e..c3aae034 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -489,14 +489,14 @@ 'sql' => "int(10) unsigned NOT NULL default '0'", ], 'minDateTime' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['minDateTime'], + 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['min'], 'exclude' => true, 'inputType' => 'text', 'eval' => ['tl_class' => 'w50 clr', 'maxlength' => 32], 'sql' => "varchar(32) NOT NULL default ''", ], 'maxDateTime' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['maxDateTime'], + 'label' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['max'], 'exclude' => true, 'inputType' => 'text', 'eval' => ['tl_class' => 'w50', 'maxlength' => 32], diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index 21e3201c..d2c1115a 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -117,6 +117,7 @@ $lang['debounce'] = ['Wartezeit nach Eingabe in ms', 'Tragen Sie hier die Wartezeit ein, die nach der Eingabe vergehen soll bevor das Formular verschickt wird.']; $lang['submitOnInput'] = ['Abschicken bei Eingabe', 'Wählen Sie diese Option, wenn das Formular abgeschickt werden soll, sobald Sie zeichen eingeben. Sie können definieren wieviele Zeichen eingegeben werden müssen um das Abschicken zu initiieren.']; $lang['doNotCacheOptions'] = ['Cache für Optionswerte deaktivieren', 'Wählen Sie diese Option, wenn die Optionswerte im Produktionsmodus nicht gecachet werden sollen.']; +$lang['buttonType'] = ['Type des Buttons', 'Wählen Sie den Typ des Buttons.']; // sort $lang['sortOptions'] = ['Sortier-Optionen', 'Fügen Sie hier die gewünschten Sortieroptionen hinzu.']; diff --git a/src/Resources/translations/messages.de.yml b/src/Resources/translations/messages.de.yml index 287b9a95..7d44e29d 100644 --- a/src/Resources/translations/messages.de.yml +++ b/src/Resources/translations/messages.de.yml @@ -1,5 +1,5 @@ # placeholders -huh.filter.placeholder.default: - +huh.filter.placeholder.default: '-' huh.filter.placeholder.input_label_name: '%label%' huh.filter.placeholder.input_label: '%label% eingeben' huh.filter.placeholder.input_label_choose: '%label% auswählen' diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 0e1256a0..5263470f 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -156,9 +156,7 @@ public function getInitialValueChoices(FilterTypeContext $filterTypeContext): ar public function getInitialValueTypes(array $types): array { - $remove = [ - AbstractFilterType::VALUE_TYPE_SCALAR, - ]; + $remove = []; return array_values(array_diff($types, $remove)); } diff --git a/tests/FilterQuery/FilterQueryPartProcessorTest.php b/tests/FilterQuery/FilterQueryPartProcessorTest.php new file mode 100644 index 00000000..7e42ec5b --- /dev/null +++ b/tests/FilterQuery/FilterQueryPartProcessorTest.php @@ -0,0 +1,53 @@ +createMock(Connection::class); + $dateUtil = $parameters['dateUtil'] ?? $this->createMock(DateUtil::class); + $databaseUtil = $parameters['databaseUtil'] ?? $this->createMock(DatabaseUtil::class); + + if ($mockBuilder) { + $instance = $this->getMockBuilder(FilterQueryPartProcessor::class) + ->setConstructorArgs([$connection, $dateUtil, $databaseUtil]) + ->getMock(); + } else { + $instance = new FilterQueryPartProcessor($connection, $dateUtil, $databaseUtil); + } + + return $instance; + } + + public function testComposeQueryPart() + { + $instance = $this->createTestInstance(); + $context = $this->createMock(FilterTypeContext::class); + $filterConfigModel = $this->createMock(FilterConfigModel::class); + $filterConfigModel->method('row')->willReturn(['dataContainer' => 'tl_news']); + $context->method('getFilterConfig')->willReturn($filterConfigModel); + + $this->assertInstanceOf(FilterQueryPart::class, $instance->composeQueryPart($context)); + } +} \ No newline at end of file From 3d60c1912a9832353a0ebbdbb8367cfbd61a0919 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 28 Jun 2021 11:10:35 +0200 Subject: [PATCH 46/58] fixed initial value of choice --- src/Backend/FilterPreselect.php | 9 +++++---- src/Type/Concrete/ChoiceType.php | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Backend/FilterPreselect.php b/src/Backend/FilterPreselect.php index 160a4420..8c3fa1d2 100644 --- a/src/Backend/FilterPreselect.php +++ b/src/Backend/FilterPreselect.php @@ -14,6 +14,7 @@ use Contao\System; use HeimrichHannot\FilterBundle\Filter\Type\ChoiceType; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; class FilterPreselect { @@ -48,12 +49,12 @@ public function adjustLabel($row, $label) $choices = $this->prepareElementChoices((int) $row['id']); switch ($row['initialValueType']) { - case \HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_SCALAR: + case AbstractFilterType::VALUE_TYPE_SCALAR: $label = $choices[$row['initialValue']] ?? $row['initialValue']; break; - case \HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_ARRAY: + case AbstractFilterType::VALUE_TYPE_ARRAY: $values = array_map( function ($item) { return $item['value'] ?? null; @@ -65,8 +66,8 @@ function ($item) { break; - case \HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_CONTEXTUAL: - $label = \HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_CONTEXTUAL; + case AbstractFilterType::VALUE_TYPE_CONTEXTUAL: + $label = AbstractFilterType::VALUE_TYPE_CONTEXTUAL; } return sprintf('%s -> %s [ID: %s]', $filterConfigElement->title, $label, $filterConfigElement->id); diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 5263470f..346bd78e 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -63,6 +63,17 @@ public function buildForm($filterTypeContext) $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); } + public function buildQuery(FilterTypeContext $filterTypeContext) + { + if ($filterTypeContext->getElementConfig()->isInitial && AbstractFilterType::VALUE_TYPE_ARRAY === $filterTypeContext->getElementConfig()->initialValueType) { + $elementConfig = $filterTypeContext->getElementConfig(); + $elementConfig->initialValue = $filterTypeContext->getElementConfig()->initialValueArray; + $filterTypeContext->setElementConfig($elementConfig); + } + + $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); + } + public function getPalette(string $prependPalette, string $appendPalette): string { return $prependPalette.'{config_legend},field,operator,customOptions,reviseOptions,dynamicOptions,sortOptionValues,adjustOptionLabels,submitOnChange,expanded,multiple,addGroupChoiceField,doNotCacheOptions;{visualization_legend},addPlaceholder,customLabel,hideLabel;'.$appendPalette; From 0ed1dd0fdb9a49d214ec6f58a7e332fb5ffca47b Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 28 Jun 2021 11:32:23 +0200 Subject: [PATCH 47/58] fixed method signatures --- src/Type/Concrete/ChoiceType.php | 2 +- src/Type/Concrete/DateTimeType.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 346bd78e..1f32b006 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -57,7 +57,7 @@ public static function getType(): string return static::TYPE; } - public function buildForm($filterTypeContext) + public function buildForm(FilterTypeContext $filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); diff --git a/src/Type/Concrete/DateTimeType.php b/src/Type/Concrete/DateTimeType.php index 824e1366..036d1d78 100644 --- a/src/Type/Concrete/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -56,7 +56,7 @@ public function buildQuery(FilterTypeContext $filterTypeContext) $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } - public function buildForm($filterTypeContext) + public function buildForm(FilterTypeContext $filterTypeContext) { $builder = $filterTypeContext->getFormBuilder(); $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyDateTimeType::class, $this->getOptions($filterTypeContext)); From c70f436908ee5ab7840628e3cf95169b5b5e8426 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 28 Jun 2021 11:52:21 +0200 Subject: [PATCH 48/58] excluded test for deprecated files --- phpunit.xml.dist | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 87b2996e..7f1237bb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -25,6 +25,15 @@ ./tests + ./tests/Backend + ./tests/Choice + ./tests/Config + ./tests/ContaoManager + ./tests/DependencyInjection + ./tests/Filter/Type + ./tests/Model + ./tests/Module + ./tests/Utils From 9bbb4bd2acee6158a7ce53f24b2cb969d080c3a1 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Fri, 23 Jul 2021 16:43:16 +0200 Subject: [PATCH 49/58] translation changes --- .../contao/dca/tl_filter_config_element.php | 15 +++++++++------ .../languages/de/tl_filter_config_element.php | 6 +++--- src/Type/Concrete/TextType.php | 4 +--- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Resources/contao/dca/tl_filter_config_element.php b/src/Resources/contao/dca/tl_filter_config_element.php index c3aae034..8b4da3d1 100644 --- a/src/Resources/contao/dca/tl_filter_config_element.php +++ b/src/Resources/contao/dca/tl_filter_config_element.php @@ -6,6 +6,8 @@ * @license LGPL-3.0-or-later */ +use HeimrichHannot\FilterBundle\Type\AbstractFilterType; + $GLOBALS['TL_DCA']['tl_filter_config_element'] = [ 'config' => [ 'dataContainer' => 'Table', @@ -170,13 +172,13 @@ 'customLanguages' => 'languages', 'customLocales' => 'locales', 'customValue' => 'value', - 'initialValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_SCALAR => 'initialValue', - 'initialValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_ARRAY => 'initialValueArray', - 'initialValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_LATEST => 'parentField', + 'initialValueType_'.AbstractFilterType::VALUE_TYPE_SCALAR => 'initialValue', + 'initialValueType_'.AbstractFilterType::VALUE_TYPE_ARRAY => 'initialValueArray', + 'initialValueType_'.AbstractFilterType::VALUE_TYPE_LATEST => 'parentField', 'addDefaultValue' => 'defaultValueType', - 'defaultValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_SCALAR => 'defaultValue', - 'defaultValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_ARRAY => 'defaultValueArray', - 'defaultValueType_'.\HeimrichHannot\FilterBundle\Filter\AbstractType::VALUE_TYPE_LATEST => 'parentField', + 'defaultValueType_'.AbstractFilterType::VALUE_TYPE_SCALAR => 'defaultValue', + 'defaultValueType_'.AbstractFilterType::VALUE_TYPE_ARRAY => 'defaultValueArray', + 'defaultValueType_'.AbstractFilterType::VALUE_TYPE_LATEST => 'parentField', 'addStartAndStop' => 'startField,stopField', 'coordinatesMode_'.\HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::COORDINATES_MODE_COMPOUND => 'coordinatesField', 'coordinatesMode_'.\HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::COORDINATES_MODE_SEPARATED => 'latField,longField', @@ -895,6 +897,7 @@ 'exclude' => true, 'search' => true, 'inputType' => 'select', + 'reference' => &$GLOBALS['TL_LANG']['tl_filter_config_element']['reference']['type'], 'options_callback' => [\HeimrichHannot\FilterBundle\DataContainer\FilterConfigElementContainer::class, 'onButtonTypeOptionsCallback'], 'eval' => [ 'tl_class' => 'w50', diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index d2c1115a..e6a65152 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -31,7 +31,7 @@ $lang['addPlaceholder'] = ['Platzhalter hinzufügen', 'Wählen Sie diese Option, um dem Filter einen Platzhaltertext hinzuzufügen.']; $lang['placeholder'] = ['Platzhalter', 'Wählen Sie hier einen Platzhalter aus.']; $lang['hideLabel'] = ['Label verstecken', 'Wählen Sie diese Option, um das Label des Filters zu verstecken.']; -$lang['customLabel'] = ['Label anpassen', 'Wählen ie diese Option, um einen benutzerdefinierten Label-Text festzulegen.']; +$lang['customLabel'] = ['Label anpassen', 'Wählen Sie diese Option, um einen benutzerdefinierten Label-Text festzulegen.']; $lang['label'] = ['Label', 'Wählen Sie hier ein Label aus.']; $lang['expanded'] = ['Expanded (Radio/Checkboxes)', 'Wählen Sie diese Option, um Optionen als "radio"- oder "checkbox"-Elemente auszugeben.']; $lang['multiple'] = ['Multiple', 'Wählen Sie diese Option, wenn der Nutzer mehrere Optionen auswählen können soll.']; @@ -117,7 +117,7 @@ $lang['debounce'] = ['Wartezeit nach Eingabe in ms', 'Tragen Sie hier die Wartezeit ein, die nach der Eingabe vergehen soll bevor das Formular verschickt wird.']; $lang['submitOnInput'] = ['Abschicken bei Eingabe', 'Wählen Sie diese Option, wenn das Formular abgeschickt werden soll, sobald Sie zeichen eingeben. Sie können definieren wieviele Zeichen eingegeben werden müssen um das Abschicken zu initiieren.']; $lang['doNotCacheOptions'] = ['Cache für Optionswerte deaktivieren', 'Wählen Sie diese Option, wenn die Optionswerte im Produktionsmodus nicht gecachet werden sollen.']; -$lang['buttonType'] = ['Type des Buttons', 'Wählen Sie den Typ des Buttons.']; +$lang['buttonType'] = ['Typ des Buttons', 'Wählen Sie den Typ des Buttons.']; // sort $lang['sortOptions'] = ['Sortier-Optionen', 'Fügen Sie hier die gewünschten Sortieroptionen hinzu.']; @@ -154,7 +154,7 @@ */ $lang['reference'] = [ 'type' => [ - 'deprecated' => 'Veraltet - NICHT nutzen', + 'deprecated' => 'Veraltet', 'miscellaneous' => 'Sonstiges', 'text' => 'Text', 'text_concat' => 'Konkatenierter Text', diff --git a/src/Type/Concrete/TextType.php b/src/Type/Concrete/TextType.php index 75157936..af405871 100644 --- a/src/Type/Concrete/TextType.php +++ b/src/Type/Concrete/TextType.php @@ -47,9 +47,7 @@ public function getInitialPalette(string $prependPalette, string $appendPalette) public function getInitialValueTypes(array $types): array { - $remove = [ - AbstractFilterType::VALUE_TYPE_ARRAY, - ]; + $remove = []; return array_values(array_diff($types, $remove)); } From 355d5268854b544b30c2d7a7d9505f11b18736c3 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 22 Nov 2021 17:10:05 +0100 Subject: [PATCH 50/58] fixed choice_type when initial --- src/Choice/FieldOptionsChoice.php | 13 ++++---- src/FilterQuery/FilterQueryPartProcessor.php | 3 +- src/Type/Concrete/ChoiceType.php | 31 ++++++++++++++++++-- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/Choice/FieldOptionsChoice.php b/src/Choice/FieldOptionsChoice.php index f9c1ae4f..55bb1ae8 100644 --- a/src/Choice/FieldOptionsChoice.php +++ b/src/Choice/FieldOptionsChoice.php @@ -14,6 +14,7 @@ use Contao\Widget; use Doctrine\DBAL\FetchMode; use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel; +use HeimrichHannot\FilterBundle\Type\FilterTypeCollection; use HeimrichHannot\UtilsBundle\Choice\AbstractChoice; use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Translation\Translator; @@ -129,7 +130,6 @@ protected function getCustomOptions(FilterConfigElementModel $element, array $fi protected function getDcaOptions(FilterConfigElementModel $element, array $filter, array $dca) { $options = []; - $dca = $GLOBALS['TL_DCA'][$filter['dataContainer']]['fields'][$element->field]; if (isset($dca['eval']['isCategoryField']) && $dca['eval']['isCategoryField']) { if (isset($dca['options_callback'])) { @@ -144,10 +144,6 @@ protected function getDcaOptions(FilterConfigElementModel $element, array $filte return $options; } - if (!isset($dca['inputType'])) { - return $options; - } - switch ($dca['inputType']) { case 'cfgTags': if (!isset($dca['eval']['tagsManager'])) { @@ -173,6 +169,13 @@ protected function getWidgetOptions(FilterConfigElementModel $element, array $fi { $options = []; + $filterTypeCollection = System::getContainer()->get(FilterTypeCollection::class); + + // fix for new filter types to show possible choices if inputType dca attribute is not given + if ($filterTypeCollection->hasType($element->type)) { + $dca['inputType'] = $element->inputType; + } + if (!isset($GLOBALS['TL_FFL'][$dca['inputType']]) && (System::getContainer()->get('huh.utils.container')->isBackend() && !isset($GLOBALS['BE_FFL'][$dca['inputType']]))) { return $options; } diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index 771c6e33..d816e51f 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -9,7 +9,6 @@ namespace HeimrichHannot\FilterBundle\FilterQuery; use Contao\Controller; -use Contao\StringUtil; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; @@ -218,7 +217,7 @@ public function updateInitialFilterProperties(FilterQueryPart $filterPart): void break; case AbstractFilterType::VALUE_TYPE_ARRAY: - $filterPart->setValue(array_column(StringUtil::deserialize($filterPart->getInitialValue()), 'value')); + $filterPart->setValue($filterPart->getInitialValue()); $filterPart->setValueType(Connection::PARAM_STR_ARRAY); break; diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 1f32b006..c3591c6a 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -8,6 +8,7 @@ namespace HeimrichHannot\FilterBundle\Type\Concrete; +use Contao\StringUtil; use Doctrine\DBAL\Driver\Connection; use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; @@ -67,7 +68,9 @@ public function buildQuery(FilterTypeContext $filterTypeContext) { if ($filterTypeContext->getElementConfig()->isInitial && AbstractFilterType::VALUE_TYPE_ARRAY === $filterTypeContext->getElementConfig()->initialValueType) { $elementConfig = $filterTypeContext->getElementConfig(); - $elementConfig->initialValue = $filterTypeContext->getElementConfig()->initialValueArray; + + $values = StringUtil::deserialize($filterTypeContext->getElementConfig()->initialValueArray, true); + $elementConfig->initialValue = array_column($values, 'value'); $filterTypeContext->setElementConfig($elementConfig); } @@ -162,7 +165,31 @@ public function getInitialPalette(string $prependPalette, string $appendPalette) public function getInitialValueChoices(FilterTypeContext $filterTypeContext): array { - return $this->collectChoices($filterTypeContext); + if (null === ($element = $filterTypeContext->getElementConfig())) { + return []; + } + + switch ($element->initialValueType) { + case AbstractFilterType::VALUE_TYPE_ARRAY: + $element->inputType = 'select'; + + break; + + case AbstractFilterType::VALUE_TYPE_SCALAR: + case AbstractFilterType::VALUE_TYPE_CONTEXTUAL: + $element->inputType = 'text'; + + break; + + case AbstractFilterType::VALUE_TYPE_LATEST: + default: + break; + } + + return $this->fieldOptionsChoice->getCachedChoices([ + 'element' => $element, + 'filter' => $filterTypeContext->getFilterConfig()->row(), + ]); } public function getInitialValueTypes(array $types): array From 9da534eb8f8a8defc8dddaa4013ddeda55e11bc3 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 23 Nov 2021 10:04:26 +0100 Subject: [PATCH 51/58] added initialValues to dateTime type --- src/FilterQuery/FilterQueryPartProcessor.php | 5 +++++ src/Type/Concrete/DateTimeType.php | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/FilterQuery/FilterQueryPartProcessor.php b/src/FilterQuery/FilterQueryPartProcessor.php index d816e51f..7cae9f31 100644 --- a/src/FilterQuery/FilterQueryPartProcessor.php +++ b/src/FilterQuery/FilterQueryPartProcessor.php @@ -11,6 +11,7 @@ use Contao\Controller; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; +use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; @@ -221,6 +222,10 @@ public function updateInitialFilterProperties(FilterQueryPart $filterPart): void $filterPart->setValueType(Connection::PARAM_STR_ARRAY); break; + + case Types::INTEGER: + $filterPart->setValue($filterPart->getInitialValue()); + $filterPart->setValueType(Types::INTEGER); } } } diff --git a/src/Type/Concrete/DateTimeType.php b/src/Type/Concrete/DateTimeType.php index 036d1d78..15e552d4 100644 --- a/src/Type/Concrete/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -50,9 +50,18 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { - $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getValue())); - $filterTypeContext->setValueType(Types::INTEGER); + if ($filterTypeContext->getElementConfig()->isInitial) { + $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getElementConfig()->initialValue)); + $filterTypeContext->getElementConfig()->initialValueType = Types::INTEGER; + } else { + $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getValue())); + } + if (empty($filterTypeContext->getValue())) { + return; + } + + $filterTypeContext->setValueType(Types::INTEGER); $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } @@ -69,7 +78,12 @@ public function getPalette(string $prependPalette, string $appendPalette): strin public function getInitialPalette(string $prependPalette, string $appendPalette) { - return $prependPalette.'{config_legend},field,operator,dateTimeFormat,defaultValue;'.$appendPalette; + $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; + $dca['fields']['initialValue']['eval']['rgxp'] = 'datim'; + $dca['fields']['initialValue']['eval']['datepicker'] = true; + $dca['fields']['initialValue']['eval']['mandatory'] = true; + + return $prependPalette.'{config_legend},field,operator,dateTimeFormat,initialValue;'.$appendPalette; } public function getInitialValueTypes(array $types): array From 9d72dfff91533f5483071e0ace04b1ea38d748dd Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Tue, 23 Nov 2021 10:26:24 +0100 Subject: [PATCH 52/58] added initial values to text type --- src/Type/Concrete/TextType.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Type/Concrete/TextType.php b/src/Type/Concrete/TextType.php index af405871..ca75f95e 100644 --- a/src/Type/Concrete/TextType.php +++ b/src/Type/Concrete/TextType.php @@ -8,6 +8,7 @@ namespace HeimrichHannot\FilterBundle\Type\Concrete; +use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; @@ -26,6 +27,18 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { + if ($filterTypeContext->getElementConfig()->isInitial) { + $filterTypeContext->setValue($filterTypeContext->getElementConfig()->initialValue); + $filterTypeContext->getElementConfig()->initialValueType = Types::STRING; + } else { + $filterTypeContext->setValue($filterTypeContext->getElementConfig()->value); + } + + if (empty($filterTypeContext->getValue())) { + return; + } + + $filterTypeContext->setValueType(Types::STRING); $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } @@ -58,7 +71,7 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $elementConfig = $filterTypeContext->getElementConfig(); - if ((bool) $elementConfig->submitOnInput && (bool)$filterTypeContext->getFilterConfig()->row()['asyncFormSubmit']) { + if ((bool) $elementConfig->submitOnInput && (bool) $filterTypeContext->getFilterConfig()->row()['asyncFormSubmit']) { $options['attr']['data-submit-on-input'] = '1'; $options['attr']['data-threshold'] = $elementConfig->threshold ?: '0'; $options['attr']['data-debounce'] = $elementConfig->debounce ?: '0'; From af7a2ae30b49c83c887489c6dcd3ce48dfcf08ea Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Wed, 1 Dec 2021 14:19:40 +0100 Subject: [PATCH 53/58] working on tests --- tests/Type/Concrete/TextTypeTest.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/Type/Concrete/TextTypeTest.php b/tests/Type/Concrete/TextTypeTest.php index a24848e8..0d77cbb0 100644 --- a/tests/Type/Concrete/TextTypeTest.php +++ b/tests/Type/Concrete/TextTypeTest.php @@ -92,7 +92,7 @@ public function testGetInitialValueTypes() $this->assertSame([], $instance->getInitialValueTypes($typesArray)); $typesArray = [AbstractFilterType::VALUE_TYPE_ARRAY]; - $this->assertSame([], $instance->getInitialValueTypes($typesArray)); + $this->assertSame($typesArray, $instance->getInitialValueTypes($typesArray)); $typesArray = [AbstractFilterType::VALUE_TYPE_CONTEXTUAL]; $this->assertSame($typesArray, $instance->getInitialValueTypes($typesArray)); @@ -101,7 +101,7 @@ public function testGetInitialValueTypes() AbstractFilterType::VALUE_TYPE_ARRAY, AbstractFilterType::VALUE_TYPE_CONTEXTUAL, ]; - $this->assertSame([AbstractFilterType::VALUE_TYPE_CONTEXTUAL], $instance->getInitialValueTypes($typesArray)); + $this->assertSame($typesArray, $instance->getInitialValueTypes($typesArray)); } public function testGetOptions() @@ -155,6 +155,15 @@ public function testBuildQuery() $this->assertEmpty($queryPartCollection->getParts()); $instance->buildQuery($filterContext); + $this->assertCount(0, $queryPartCollection->getParts()); + + $context = new FilterTypeContext(); + $context->setValue('text'); +// $instance + + $elementConfig = $this->createMock(FilterConfigElementModel::class); + $context->setElementConfig($elementConfig); + $instance->buildQuery($context); $this->assertCount(1, $queryPartCollection->getParts()); } From c26d54057d3169ed7d41c298043b5ad385cab588 Mon Sep 17 00:00:00 2001 From: Alexej Kossmann Date: Mon, 6 Dec 2021 16:42:41 +0100 Subject: [PATCH 54/58] fixed filters if legacy and new filter are mixed on one page --- src/Config/FilterConfig.php | 33 ++++++++++++++----- src/Filter/Type/ParentType.php | 2 +- src/Resources/config/config.yml | 2 +- .../languages/de/tl_filter_config_element.php | 2 +- .../languages/en/tl_filter_config_element.php | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Config/FilterConfig.php b/src/Config/FilterConfig.php index 7996dcc7..3f2458e2 100644 --- a/src/Config/FilterConfig.php +++ b/src/Config/FilterConfig.php @@ -13,8 +13,10 @@ use Contao\Environment; use Contao\InsertTags; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Types\Types; use HeimrichHannot\FilterBundle\Event\ModifyFilterQueryPartsEvent; use HeimrichHannot\FilterBundle\Filter\AbstractType; +use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPart; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension; @@ -40,7 +42,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\PropertyAccess\PropertyAccess; -use System; class FilterConfig implements \JsonSerializable { @@ -262,7 +263,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip } $types = array_merge($this->container->get('huh.filter.choice.type')->getCachedChoices(), - System::getContainer()->get(FilterTypeCollection::class)->getTypes()); + $this->container->get(FilterTypeCollection::class)->getTypes()); if (!\is_array($types) || empty($types)) { return; @@ -285,7 +286,7 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip continue; } - if (!class_exists($types[$element->type]['class'])) { + if (!class_exists($types[$element->type]['class'], false)) { continue; } @@ -331,8 +332,8 @@ public function doInitQueryBuilder(FilterQueryBuilder $queryBuilder, array $skip $this->prepareFilterQueryParts($event->getPartsCollection()); - /* - * @var FilterQueryPart + /** + * @var $part FilterQueryPart */ foreach ($this->filterQueryPartCollection->getParts() as $part) { if ($part->isDisabled()) { @@ -728,7 +729,7 @@ protected function processFilterType(FilterConfigElementModel $config, FilterTyp protected function processLegacyFilterType(FilterConfigElementModel $config, array $element) { - if (!$this->getData()[$config->field]) { + if (!$this->getData()[$config->field] && !(bool) $config->isInitial) { return; } @@ -745,7 +746,23 @@ protected function processLegacyFilterType(FilterConfigElementModel $config, arr } $context = new FilterTypeContext(); - $context->setValue($this->getData()[$config->field]); + + switch ($config->initialValueType) { + case AbstractType::VALUE_TYPE_ARRAY: + $context->setValue($config->initialValueArray); + $context->setValueType(Connection::PARAM_STR_ARRAY); + $config->initialValueType = Connection::PARAM_STR_ARRAY; + + break; + + default: + $context->setValue($config->initialValue); + $context->setValueType(Types::STRING); + $config->initialValueType = Types::STRING; + + break; + } + $context->setElementConfig($config); $context->setFilterConfig($config->getRelated('pid')); @@ -823,7 +840,7 @@ protected function mapFormsToData() private function prepareFilterQueryParts(FilterQueryPartCollection $filterQueryPartCollection) { foreach ($filterQueryPartCollection->getTargetFields() as $targetField) { - if (1 >= count($targetField)) { + if (1 >= \count($targetField)) { continue; } diff --git a/src/Filter/Type/ParentType.php b/src/Filter/Type/ParentType.php index 6509709a..7c60311b 100644 --- a/src/Filter/Type/ParentType.php +++ b/src/Filter/Type/ParentType.php @@ -17,7 +17,7 @@ */ class ParentType extends ChoiceType { - const TYPE = 'filterConfig'; + const TYPE = 'parent'; /** {@inheritdoc} */ public function getChoices(FilterConfigElementModel $element) diff --git a/src/Resources/config/config.yml b/src/Resources/config/config.yml index 2dfea40e..1e84f731 100644 --- a/src/Resources/config/config.yml +++ b/src/Resources/config/config.yml @@ -22,7 +22,7 @@ huh: - { name: proximity_search, class: HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType, type: other, wrapper: true } - { name: language, class: HeimrichHannot\FilterBundle\Filter\Type\LanguageType, type: choice } - { name: locale, class: HeimrichHannot\FilterBundle\Filter\Type\LocaleType, type: choice } - - { name: filterConfig, class: HeimrichHannot\FilterBundle\Filter\Type\ParentType, type: choice } + - { name: parent, class: HeimrichHannot\FilterBundle\Filter\Type\ParentType, type: choice } - { name: skip_parents, class: HeimrichHannot\FilterBundle\Filter\Type\SkipParentsType, type: other } - { name: visible, class: HeimrichHannot\FilterBundle\Filter\Type\PublishedType, type: other } - { name: button, class: HeimrichHannot\FilterBundle\Filter\Type\ButtonType, type: button } diff --git a/src/Resources/contao/languages/de/tl_filter_config_element.php b/src/Resources/contao/languages/de/tl_filter_config_element.php index e6a65152..de3cedb5 100644 --- a/src/Resources/contao/languages/de/tl_filter_config_element.php +++ b/src/Resources/contao/languages/de/tl_filter_config_element.php @@ -177,7 +177,7 @@ \HeimrichHannot\FilterBundle\Filter\Type\ProximitySearchType::TYPE => 'Umkreissuche', 'language' => 'Sprache', 'locale' => 'Region ("locale")', - 'filterConfig' => 'Elternentität', + 'parent' => 'Elternentität', 'skip_parents' => 'Elternentitäten ausschließen', 'visible' => 'Veröffentlicht', 'button' => 'Button', diff --git a/src/Resources/contao/languages/en/tl_filter_config_element.php b/src/Resources/contao/languages/en/tl_filter_config_element.php index 51597f4a..1f93c245 100644 --- a/src/Resources/contao/languages/en/tl_filter_config_element.php +++ b/src/Resources/contao/languages/en/tl_filter_config_element.php @@ -132,7 +132,7 @@ 'country' => 'Country', 'language' => 'Language', 'locale' => 'Locale', - 'filterConfig' => 'Parent entity', + 'parent' => 'Parent entity', 'published' => 'Published', 'button' => 'Button', 'reset' => 'Reset', From a880a81bdf33d787905bee318ad725f2fdf6ed49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Tue, 7 Dec 2021 09:51:40 +0100 Subject: [PATCH 55/58] refactored service loading in FilterTypes to ServiceSubscriber for better expandability, fixed contao 4.4 support --- composer.json | 2 +- src/Resources/config/services.yml | 1 + src/Type/AbstractFilterType.php | 25 ++++++++++++---- src/Type/Concrete/ChoiceType.php | 47 +++++++++--------------------- src/Type/Concrete/DateTimeType.php | 39 ++++++++++++------------- 5 files changed, 54 insertions(+), 60 deletions(-) diff --git a/composer.json b/composer.json index 2bac83ae..aefc068a 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "contao/core-bundle": "^4.4", "symfony/framework-bundle": "~3.4.26|^4.0", "heimrichhannot/contao-be_explanation-bundle": "^2.3", - "heimrichhannot/contao-utils-bundle": "^2.0", + "heimrichhannot/contao-utils-bundle": "^2.206", "heimrichhannot/contao-multi-column-editor-bundle": "^2.0", "heimrichhannot/contao-fieldpalette-bundle": ">=0.2 <2.0-dev", "heimrichhannot/contao-entity-filter-bundle": "^1.5", diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 9456048a..ef052344 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -172,6 +172,7 @@ services: HeimrichHannot\FilterBundle\Type\Concrete\: resource: '../../Type/Concrete/*' + autoconfigure: true tags: ['huh.filter.type.concrete'] HeimrichHannot\FilterBundle\Type\FilterTypeCollection: diff --git a/src/Type/AbstractFilterType.php b/src/Type/AbstractFilterType.php index 25d685f9..daa66b4b 100644 --- a/src/Type/AbstractFilterType.php +++ b/src/Type/AbstractFilterType.php @@ -11,9 +11,11 @@ use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; -use Symfony\Contracts\Translation\TranslatorInterface; +use HeimrichHannot\UtilsBundle\Util\AbstractServiceSubscriber; +use Psr\Container\ContainerInterface; +use Symfony\Component\Translation\TranslatorInterface; -abstract class AbstractFilterType implements FilterTypeInterface +abstract class AbstractFilterType extends AbstractServiceSubscriber implements FilterTypeInterface { const GROUP_DEFAULT = 'miscellaneous'; @@ -48,15 +50,19 @@ abstract class AbstractFilterType implements FilterTypeInterface */ private $group = ''; + /** @var ContainerInterface */ + protected $container; + public function __construct( + ContainerInterface $container, FilterQueryPartProcessor $filterQueryPartProcessor, - FilterQueryPartCollection $filterQueryPartCollection, - TranslatorInterface $translator + FilterQueryPartCollection $filterQueryPartCollection ) { $this->initialize(); $this->filterQueryPartProcessor = $filterQueryPartProcessor; $this->filterQueryPartCollection = $filterQueryPartCollection; - $this->translator = $translator; + $this->container = $container; + $this->translator = $this->container->get('translator'); } public function getPalette(string $prependPalette, string $appendPalette): string @@ -157,4 +163,13 @@ protected function initialize(): void $this->setGroup(static::GROUP); } } + + public static function getSubscribedServices() + { + return [ + 'translator' => 'translator', + ]; + } + + } diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index c3591c6a..0dc2e5be 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -11,48 +11,18 @@ use Contao\StringUtil; use Doctrine\DBAL\Driver\Connection; use HeimrichHannot\FilterBundle\Choice\FieldOptionsChoice; -use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartCollection; -use HeimrichHannot\FilterBundle\FilterQuery\FilterQueryPartProcessor; use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; use HeimrichHannot\FilterBundle\Type\InitialFilterTypeInterface; use HeimrichHannot\UtilsBundle\Database\DatabaseUtil; use HeimrichHannot\UtilsBundle\Model\ModelUtil; use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; -use Symfony\Contracts\Translation\TranslatorInterface; -class ChoiceType extends AbstractFilterType implements InitialFilterTypeInterface +class ChoiceType extends AbstractFilterType implements InitialFilterTypeInterface { const TYPE = 'choice_type'; const GROUP = 'choice'; - /** - * @var FieldOptionsChoice - */ - protected $fieldOptionsChoice; - /** - * @var ModelUtil - */ - protected $modelUtil; - /** - * @var Connection - */ - protected $connection; - - public function __construct( - FilterQueryPartProcessor $filterQueryPartProcessor, - FilterQueryPartCollection $filterQueryPartCollection, - TranslatorInterface $translator, - FieldOptionsChoice $fieldOptionsChoice, - ModelUtil $modelUtil, - Connection $connection - ) { - parent::__construct($filterQueryPartProcessor, $filterQueryPartCollection, $translator); - $this->fieldOptionsChoice = $fieldOptionsChoice; - $this->modelUtil = $modelUtil; - $this->connection = $connection; - } - public static function getType(): string { return static::TYPE; @@ -152,7 +122,7 @@ public function collectChoices(FilterTypeContext $filterTypeContext): array return []; } - return $this->fieldOptionsChoice->getCachedChoices([ + return $this->container->get('fieldOptionsChoice')->getCachedChoices([ 'element' => $filterTypeContext->getElementConfig(), 'filter' => $filterTypeContext->getFilterConfig()->row(), ]); @@ -186,7 +156,7 @@ public function getInitialValueChoices(FilterTypeContext $filterTypeContext): ar break; } - return $this->fieldOptionsChoice->getCachedChoices([ + return $this->container->get('fieldOptionsChoice')->getCachedChoices([ 'element' => $element, 'filter' => $filterTypeContext->getFilterConfig()->row(), ]); @@ -198,4 +168,15 @@ public function getInitialValueTypes(array $types): array return array_values(array_diff($types, $remove)); } + + public static function getSubscribedServices() + { + return array_merge(parent::getSubscribedServices(), [ + 'fieldOptionsChoice' => FieldOptionsChoice::class, + 'modalUtil' => ModelUtil::class, + 'connection' => Connection::class, + ]); + } + + } diff --git a/src/Type/Concrete/DateTimeType.php b/src/Type/Concrete/DateTimeType.php index 15e552d4..d37be602 100644 --- a/src/Type/Concrete/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -33,16 +33,6 @@ class DateTimeType extends AbstractFilterType implements InitialFilterTypeInterf */ protected $dateUtil; - public function __construct( - FilterQueryPartProcessor $filterQueryPartProcessor, - FilterQueryPartCollection $filterQueryPartCollection, - TranslatorInterface $translator, - DateUtil $dateUtil - ) { - parent::__construct($filterQueryPartProcessor, $filterQueryPartCollection, $translator); - $this->dateUtil = $dateUtil; - } - public static function getType(): string { return static::TYPE; @@ -51,10 +41,10 @@ public static function getType(): string public function buildQuery(FilterTypeContext $filterTypeContext) { if ($filterTypeContext->getElementConfig()->isInitial) { - $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getElementConfig()->initialValue)); + $filterTypeContext->setValue($this->container->get('dateUtil')->getTimeStamp($filterTypeContext->getElementConfig()->initialValue)); $filterTypeContext->getElementConfig()->initialValueType = Types::INTEGER; } else { - $filterTypeContext->setValue($this->dateUtil->getTimeStamp($filterTypeContext->getValue())); + $filterTypeContext->setValue($this->container->get('dateUtil')->getTimeStamp($filterTypeContext->getValue())); } if (empty($filterTypeContext->getValue())) { @@ -122,30 +112,30 @@ public function getOptions(FilterTypeContext $filterTypeContext): array case static::WIDGET_TYPE_SINGLE_TEXT: if ($elementConfig->html5) { $options['html5'] = $elementConfig->html5; - $options['date_format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + $options['date_format'] = $this->container->get('dateUtil')->transformPhpDateFormatToRFC3339($format); if ('' !== $elementConfig->minDateTime) { - $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used + $options['attr']['min'] = Date::parse('Y-m-d\TH:i', $this->container->get('dateUtil')->getTimeStamp($elementConfig->minDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used } if ('' !== $elementConfig->maxDateTime) { - $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used + $options['attr']['max'] = Date::parse('Y-m-d\TH:i', $this->container->get('dateUtil')->getTimeStamp($elementConfig->maxDateTime)); // valid rfc 3339 date `YYYY-MM-DD` format must be used } } else { - $options['format'] = $this->dateUtil->transformPhpDateFormatToRFC3339($format); + $options['format'] = $this->container->get('dateUtil')->transformPhpDateFormatToRFC3339($format); } $options['group_attr']['class'] = isset($options['group_attr']['class']) ? $options['group_attr']['class'].' datepicker timepicker' : 'datepicker timepicker'; - $options['attr']['data-iso8601-format'] = $this->dateUtil->transformPhpDateFormatToISO8601($format); + $options['attr']['data-iso8601-format'] = $this->container->get('dateUtil')->transformPhpDateFormatToISO8601($format); $options['attr']['data-enable-time'] = 'true'; $options['attr']['data-date-format'] = $format; if ('' !== $elementConfig->minDateTime) { - $options['attr']['data-min-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); + $options['attr']['data-min-date'] = Date::parse($format, $this->container->get('dateUtil')->getTimeStamp($elementConfig->minDateTime)); } if ('' !== $elementConfig->maxDateTime) { - $options['attr']['data-max-date'] = Date::parse($format, $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); + $options['attr']['data-max-date'] = Date::parse($format, $this->container->get('dateUtil')->getTimeStamp($elementConfig->maxDateTime)); } break; @@ -159,11 +149,11 @@ public function getOptions(FilterTypeContext $filterTypeContext): array $maxYear = Date::parse('Y', strtotime('+5 year', $time)); if ('' !== $elementConfig->minDateTime) { - $minYear = Date::parse('Y', $this->dateUtil->getTimeStamp($elementConfig->minDateTime)); + $minYear = Date::parse('Y', $this->container->get('dateUtil')->getTimeStamp($elementConfig->minDateTime)); } if ('' !== $elementConfig->maxDateTime) { - $maxYear = Date::parse('Y', $this->dateUtil->getTimeStamp($elementConfig->maxDateTime)); + $maxYear = Date::parse('Y', $this->container->get('dateUtil')->getTimeStamp($elementConfig->maxDateTime)); } $options['years'] = range($minYear, $maxYear, 1); @@ -179,4 +169,11 @@ public function getOptions(FilterTypeContext $filterTypeContext): array return $options; } + + public static function getSubscribedServices() + { + return array_merge(parent::getSubscribedServices(), [ + 'dateUtil' => DateUtil::class, + ]); + } } From 8e5accf18c35a2830250db664387c36008156af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Tue, 7 Dec 2021 10:07:49 +0100 Subject: [PATCH 56/58] added psr/container to dependencies --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index aefc068a..b74e45cd 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "twig/extensions": "^1.5", "heimrichhannot/truncate-html": "^1.0", "symfony/form": "~3.4|~4.0", - "ext-pdo": "*" + "ext-pdo": "*", + "psr/container": "^1.0 || ^2.0" }, "require-dev": { "contao/core-bundle": "4.4.*", From 6b8b8f578c02c102b15e141ec377348f3848d7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Tue, 7 Dec 2021 10:22:24 +0100 Subject: [PATCH 57/58] fix tests --- tests/Type/Concrete/TextTypeTest.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/Type/Concrete/TextTypeTest.php b/tests/Type/Concrete/TextTypeTest.php index 0d77cbb0..9976703a 100644 --- a/tests/Type/Concrete/TextTypeTest.php +++ b/tests/Type/Concrete/TextTypeTest.php @@ -17,6 +17,8 @@ use HeimrichHannot\FilterBundle\Type\AbstractFilterType; use HeimrichHannot\FilterBundle\Type\Concrete\TextType; use HeimrichHannot\FilterBundle\Type\FilterTypeContext; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormFactoryInterface; @@ -41,13 +43,22 @@ public function createTestInstance(array $parameters = [], $mockBuilder = false) $processor = $parameters['processor'] ?? $this->createMock(FilterQueryPartProcessor::class); $collection = $parameters['collection'] ?? $this->createMock(FilterQueryPartCollection::class); + $container = $this->createMock(ContainerInterface::class); + $container->method('get')->willReturnCallback(function ($argument) use ($translator) { + switch ($argument) { + case 'translator': + return $translator; + }; + return null; + }); + if ($mockBuilder) { $instance = $this->getMockBuilder(TextType::class) - ->setConstructorArgs([$processor, $collection, $translator]) + ->setConstructorArgs([$container, $processor, $collection]) ->setMethods(['getOptions', 'buildForm']) ->getMock(); } else { - $instance = new TextType($processor, $collection, $translator); + $instance = new TextType($container, $processor, $collection); } return $instance; From ea3e9f6a85d1d48c5cc9db6b5e8f6b911218b341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6rner?= Date: Tue, 7 Dec 2021 13:41:47 +0100 Subject: [PATCH 58/58] added missing method to FilterTypeInterface and added return types --- src/Model/FilterConfigElementModel.php | 3 +++ src/Type/AbstractFilterType.php | 2 +- src/Type/Concrete/ButtonType.php | 4 +-- src/Type/Concrete/ChoiceType.php | 4 +-- src/Type/Concrete/DateTimeType.php | 4 +-- src/Type/Concrete/TextType.php | 2 +- src/Type/Concrete/YearType.php | 34 +++++++++++++++++++++++++ src/Type/FilterTypeInterface.php | 4 ++- src/Type/InitialFilterTypeInterface.php | 2 +- 9 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 src/Type/Concrete/YearType.php diff --git a/src/Model/FilterConfigElementModel.php b/src/Model/FilterConfigElementModel.php index c4e18ea0..c73b0128 100644 --- a/src/Model/FilterConfigElementModel.php +++ b/src/Model/FilterConfigElementModel.php @@ -278,6 +278,9 @@ public function jsonSerialize() public function getElementName(): string { + if (true === (bool) $this->customName && '' !== $this->name) { + return $this->name; + } return $this->type.'_'.$this->id; } } diff --git a/src/Type/AbstractFilterType.php b/src/Type/AbstractFilterType.php index daa66b4b..77eb5820 100644 --- a/src/Type/AbstractFilterType.php +++ b/src/Type/AbstractFilterType.php @@ -100,7 +100,7 @@ public function getOperators(): array ]; } - public function buildQuery(FilterTypeContext $filterTypeContext) + public function buildQuery(FilterTypeContext $filterTypeContext): void { $this->filterQueryPartCollection->addPart($this->filterQueryPartProcessor->composeQueryPart($filterTypeContext)); } diff --git a/src/Type/Concrete/ButtonType.php b/src/Type/Concrete/ButtonType.php index cee8befc..9eb7a27c 100644 --- a/src/Type/Concrete/ButtonType.php +++ b/src/Type/Concrete/ButtonType.php @@ -33,9 +33,9 @@ public static function getType(): string return static::TYPE; } - public function buildQuery(FilterTypeContext $filterTypeContext): string + public function buildQuery(FilterTypeContext $filterTypeContext): void { - return ''; + // no not add something to the query } public function buildForm(FilterTypeContext $filterTypeContext) diff --git a/src/Type/Concrete/ChoiceType.php b/src/Type/Concrete/ChoiceType.php index 0dc2e5be..d0702331 100644 --- a/src/Type/Concrete/ChoiceType.php +++ b/src/Type/Concrete/ChoiceType.php @@ -34,7 +34,7 @@ public function buildForm(FilterTypeContext $filterTypeContext) $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); } - public function buildQuery(FilterTypeContext $filterTypeContext) + public function buildQuery(FilterTypeContext $filterTypeContext): void { if ($filterTypeContext->getElementConfig()->isInitial && AbstractFilterType::VALUE_TYPE_ARRAY === $filterTypeContext->getElementConfig()->initialValueType) { $elementConfig = $filterTypeContext->getElementConfig(); @@ -49,7 +49,7 @@ public function buildQuery(FilterTypeContext $filterTypeContext) public function getPalette(string $prependPalette, string $appendPalette): string { - return $prependPalette.'{config_legend},field,operator,customOptions,reviseOptions,dynamicOptions,sortOptionValues,adjustOptionLabels,submitOnChange,expanded,multiple,addGroupChoiceField,doNotCacheOptions;{visualization_legend},addPlaceholder,customLabel,hideLabel;'.$appendPalette; + return $prependPalette.'{config_legend},field,operator,customOptions,reviseOptions,dynamicOptions,sortOptionValues,customName,adjustOptionLabels,submitOnChange,expanded,multiple,addGroupChoiceField,doNotCacheOptions;{visualization_legend},addPlaceholder,customLabel,hideLabel;'.$appendPalette; } public function getOperators(): array diff --git a/src/Type/Concrete/DateTimeType.php b/src/Type/Concrete/DateTimeType.php index d37be602..bec8f39d 100644 --- a/src/Type/Concrete/DateTimeType.php +++ b/src/Type/Concrete/DateTimeType.php @@ -38,7 +38,7 @@ public static function getType(): string return static::TYPE; } - public function buildQuery(FilterTypeContext $filterTypeContext) + public function buildQuery(FilterTypeContext $filterTypeContext): void { if ($filterTypeContext->getElementConfig()->isInitial) { $filterTypeContext->setValue($this->container->get('dateUtil')->getTimeStamp($filterTypeContext->getElementConfig()->initialValue)); @@ -66,7 +66,7 @@ public function getPalette(string $prependPalette, string $appendPalette): strin return $prependPalette.'{config_legend},field,operator,dateTimeFormat,minDateTime,maxDateTime;{visualization_legend},html5,dateWidget,customLabel,hideLabel,addPlaceholder;'.$appendPalette; } - public function getInitialPalette(string $prependPalette, string $appendPalette) + public function getInitialPalette(string $prependPalette, string $appendPalette): string { $dca = &$GLOBALS['TL_DCA']['tl_filter_config_element']; $dca['fields']['initialValue']['eval']['rgxp'] = 'datim'; diff --git a/src/Type/Concrete/TextType.php b/src/Type/Concrete/TextType.php index ca75f95e..1c96c248 100644 --- a/src/Type/Concrete/TextType.php +++ b/src/Type/Concrete/TextType.php @@ -25,7 +25,7 @@ public static function getType(): string return static::TYPE; } - public function buildQuery(FilterTypeContext $filterTypeContext) + public function buildQuery(FilterTypeContext $filterTypeContext): void { if ($filterTypeContext->getElementConfig()->isInitial) { $filterTypeContext->setValue($filterTypeContext->getElementConfig()->initialValue); diff --git a/src/Type/Concrete/YearType.php b/src/Type/Concrete/YearType.php new file mode 100644 index 00000000..149deea4 --- /dev/null +++ b/src/Type/Concrete/YearType.php @@ -0,0 +1,34 @@ +getFormBuilder(); + $builder->add($filterTypeContext->getElementConfig()->getElementName(), SymfonyChoiceType::class, $this->getOptions($filterTypeContext)); + } + + public function getInitialPalette(string $prependPalette, string $appendPalette): string + { + return $prependPalette.$appendPalette; + } + + public function getInitialValueTypes(array $types): array + { + return []; + } + + public static function getType(): string + { + return static::TYPE; + } +} \ No newline at end of file diff --git a/src/Type/FilterTypeInterface.php b/src/Type/FilterTypeInterface.php index b81ff02b..8488a1ca 100644 --- a/src/Type/FilterTypeInterface.php +++ b/src/Type/FilterTypeInterface.php @@ -10,7 +10,9 @@ interface FilterTypeInterface { - public function buildQuery(FilterTypeContext $filterTypeContext); + public static function getType(): string; + + public function buildQuery(FilterTypeContext $filterTypeContext): void; public function buildForm(FilterTypeContext $filterTypeContext); diff --git a/src/Type/InitialFilterTypeInterface.php b/src/Type/InitialFilterTypeInterface.php index 4cc4fd61..6c96f06a 100644 --- a/src/Type/InitialFilterTypeInterface.php +++ b/src/Type/InitialFilterTypeInterface.php @@ -10,7 +10,7 @@ interface InitialFilterTypeInterface { - public function getInitialPalette(string $prependPalette, string $appendPalette); + public function getInitialPalette(string $prependPalette, string $appendPalette): string; public function getInitialValueTypes(array $types): array; }