diff --git a/Makefile b/Makefile
index 708d42a..68e280d 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ install:
## fix php code style
ecs:
- vendor/bin/ecs --fix
+ vendor/bin/ecs --fix --clear-cache
## check code with phpstan
phpstan:
diff --git a/composer.json b/composer.json
index 6850d55..4874aca 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,8 @@
"araise/core-bundle": "^1.0",
"araise/search-bundle": "^3.0",
"phpoffice/phpspreadsheet": "^1.22",
- "symfony/stimulus-bundle": "^2.10"
+ "symfony/stimulus-bundle": "^2.10",
+ "coduo/php-to-string": "^3.2"
},
"require-dev": {
"symfony/phpunit-bridge": "^v6.0",
diff --git a/src/Extension/FilterExtension.php b/src/Extension/FilterExtension.php
index 6dcef45..8fbd2a3 100644
--- a/src/Extension/FilterExtension.php
+++ b/src/Extension/FilterExtension.php
@@ -34,8 +34,10 @@
use araise\TableBundle\Entity\Filter as FilterEntity;
use araise\TableBundle\Exception\InvalidFilterAcronymException;
use araise\TableBundle\Filter\FilterGuesser;
+use araise\TableBundle\Filter\Type\FilterType;
use araise\TableBundle\Filter\Type\FilterTypeInterface;
use araise\TableBundle\Helper\RouterHelper;
+use araise\TableBundle\Manager\FilterTypeManager;
use araise\TableBundle\Table\Filter;
use araise\TableBundle\Table\Table;
use Doctrine\Common\Annotations\AnnotationException;
@@ -69,6 +71,7 @@ class FilterExtension extends AbstractExtension
public function __construct(
protected EntityManagerInterface $entityManager,
+ protected FilterTypeManager $filterTypeManager,
protected RequestStack $requestStack,
protected FilterGuesser $filterGuesser,
protected LoggerInterface $logger
@@ -95,6 +98,7 @@ public function configureOptions(OptionsResolver $resolver): void
/**
* @return $this
+ * @interal use addFilterType instead
*/
public function addFilter(string $acronym, string $label, FilterTypeInterface $type)
{
@@ -103,6 +107,19 @@ public function addFilter(string $acronym, string $label, FilterTypeInterface $t
return $this;
}
+ /**
+ * @return $this
+ */
+ public function addFilterType(string $acronym, string $label, string $typeClass, array $options = [])
+ {
+ if (!array_key_exists(FilterType::OPT_COLUMN, $options)) {
+ $options[FilterType::OPT_COLUMN] = $acronym;
+ }
+ $type = $this->filterTypeManager->getFilterType($typeClass)->setOptions($options);
+ $this->filters[$acronym] = new Filter($acronym, $label, $type);
+ return $this;
+ }
+
/**
* @return $this
*/
diff --git a/src/Filter/FilterGuesser.php b/src/Filter/FilterGuesser.php
index f53255c..ccd2f00 100644
--- a/src/Filter/FilterGuesser.php
+++ b/src/Filter/FilterGuesser.php
@@ -35,9 +35,11 @@
use araise\TableBundle\Filter\Type\BooleanFilterType;
use araise\TableBundle\Filter\Type\DateFilterType;
use araise\TableBundle\Filter\Type\DatetimeFilterType;
+use araise\TableBundle\Filter\Type\FilterType;
use araise\TableBundle\Filter\Type\FilterTypeInterface;
use araise\TableBundle\Filter\Type\NumberFilterType;
use araise\TableBundle\Filter\Type\TextFilterType;
+use araise\TableBundle\Manager\FilterTypeManager;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\Column;
@@ -65,7 +67,8 @@ class FilterGuesser
];
public function __construct(
- protected EntityManagerInterface $entityManager
+ protected EntityManagerInterface $entityManager,
+ protected FilterTypeManager $filterTypeManager,
) {
}
@@ -102,7 +105,9 @@ private function newColumnFilter(string $type, string $accessor): ?FilterTypeInt
return null;
}
- return new (self::SCALAR_TYPES[$type])($accessor);
+ return $this->filterTypeManager->getFilterType(self::SCALAR_TYPES[$type])->setOptions([
+ FilterType::OPT_COLUMN => $accessor,
+ ]);
}
private function newManyToManyFilter(string $class, string $acronym, string $targetEntity, callable $jsonSearchCallable, array $joins): ?FilterTypeInterface
@@ -111,13 +116,12 @@ private function newManyToManyFilter(string $class, string $acronym, string $tar
return null;
}
- return new (self::RELATION_TYPE[$class])(
- $acronym,
- $targetEntity,
- $this->entityManager,
- $jsonSearchCallable($targetEntity),
- $joins
- );
+ return $this->filterTypeManager->getFilterType(self::RELATION_TYPE[$class])->setOptions([
+ FilterType::OPT_COLUMN => $acronym,
+ FilterType::OPT_JOINS => $joins,
+ AjaxOneToManyFilterType::OPT_TARGET_CLASS => $targetEntity,
+ AjaxOneToManyFilterType::OPT_JSON_SEARCH_URL => $jsonSearchCallable($targetEntity),
+ ]);
}
private function newRelationFilter(string $class, string $acronym, string $targetEntity, string $namespace, callable $jsonSearchCallable, array $joins): ?FilterTypeInterface
@@ -130,16 +134,15 @@ private function newRelationFilter(string $class, string $acronym, string $targe
$targetEntity = $namespace.'\\'.$targetEntity;
}
- return new (self::RELATION_TYPE[$class])(
- $acronym,
- $targetEntity,
- $this->entityManager,
- $jsonSearchCallable($targetEntity),
- $joins
- );
+ return $this->filterTypeManager->getFilterType(self::RELATION_TYPE[$class])->setOptions([
+ FilterType::OPT_COLUMN => $acronym,
+ FilterType::OPT_JOINS => $joins,
+ AjaxRelationFilterType::OPT_TARGET_CLASS => $targetEntity,
+ AjaxRelationFilterType::OPT_JSON_SEARCH_URL => $jsonSearchCallable($targetEntity),
+ ]);
}
- private function getAnnotationsAndAttributes(\ReflectionProperty $property): ?array
+ private function getAnnotationsAndAttributes(\ReflectionProperty $property): array
{
$annotations = (new AnnotationReader())->getPropertyAnnotations($property);
$attributes = $property->getAttributes();
diff --git a/src/Filter/Type/AjaxManyToManyFilterType.php b/src/Filter/Type/AjaxManyToManyFilterType.php
index e5fe31b..e6b3310 100644
--- a/src/Filter/Type/AjaxManyToManyFilterType.php
+++ b/src/Filter/Type/AjaxManyToManyFilterType.php
@@ -35,19 +35,17 @@ class AjaxManyToManyFilterType extends AjaxOneToManyFilterType
{
public function toDql(string $operator, string $value, string $parameterName, QueryBuilder $queryBuilder)
{
+ $targetClass = $this->getOption(static::OPT_TARGET_CLASS);
$targetParameter = 'target_'.hash('crc32', random_bytes(10));
$queryBuilder->setParameter(
$targetParameter,
- $this->entityManager->getRepository($this->targetClass)->find($value)
+ $this->entityManager->getRepository($targetClass)->find($value)
);
-
- switch ($operator) {
- case static::CRITERIA_EQUAL:
- return $queryBuilder->expr()->isMemberOf(':'.$targetParameter, $this->column);
- case static::CRITERIA_NOT_EQUAL:
- return $queryBuilder->expr()->not($queryBuilder->expr()->isMemberOf($targetParameter, $this->column));
- }
-
- return null;
+ $column = $this->getOption(static::OPT_COLUMN);
+ return match ($operator) {
+ static::CRITERIA_EQUAL => $queryBuilder->expr()->isMemberOf(':'.$targetParameter, $column),
+ static::CRITERIA_NOT_EQUAL => $queryBuilder->expr()->not($queryBuilder->expr()->isMemberOf($targetParameter, $column)),
+ default => null,
+ };
}
}
diff --git a/src/Filter/Type/AjaxOneToManyFilterType.php b/src/Filter/Type/AjaxOneToManyFilterType.php
index f7b6fde..00c705d 100644
--- a/src/Filter/Type/AjaxOneToManyFilterType.php
+++ b/src/Filter/Type/AjaxOneToManyFilterType.php
@@ -29,8 +29,10 @@
namespace araise\TableBundle\Filter\Type;
+use Coduo\ToString\StringConverter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\OptionsResolver\OptionsResolver;
class AjaxOneToManyFilterType extends FilterType
{
@@ -38,14 +40,14 @@ class AjaxOneToManyFilterType extends FilterType
public const CRITERIA_NOT_EQUAL = 'not_equal';
+ public const OPT_TARGET_CLASS = 'target_class';
+
+ public const OPT_JSON_SEARCH_URL = 'json_search_url';
+
public function __construct(
- string $column,
- protected string $targetClass,
protected EntityManagerInterface $entityManager,
- protected string $jsonSearchUrl,
- array $joins = []
) {
- parent::__construct($column, $joins);
+ parent::__construct();
}
public function getOperators(): array
@@ -58,14 +60,15 @@ public function getOperators(): array
public function getValueField(?string $value = '0'): string
{
+ $targetClass = $this->getOption(static::OPT_TARGET_CLASS);
$field = sprintf(
'';
@@ -75,19 +78,29 @@ public function getValueField(?string $value = '0'): string
public function toDql(string $operator, string $value, string $parameterName, QueryBuilder $queryBuilder)
{
+ $targetClass = $this->getOption(static::OPT_TARGET_CLASS);
$targetParameter = 'target_'.hash('crc32', random_bytes(10));
$queryBuilder->setParameter(
$targetParameter,
- $this->entityManager->getRepository($this->targetClass)->find($value)
+ $this->entityManager->getRepository($targetClass)->find($value)
);
- switch ($operator) {
- case static::CRITERIA_EQUAL:
- return $queryBuilder->expr()->in($this->column, ':'.$targetParameter);
- case static::CRITERIA_NOT_EQUAL:
- return $queryBuilder->expr()->notIn($this->column, ':'.$targetParameter);
- }
+ $column = $this->getOption(static::OPT_COLUMN);
+ return match ($operator) {
+ static::CRITERIA_EQUAL => $queryBuilder->expr()->in($column, ':'.$targetParameter),
+ static::CRITERIA_NOT_EQUAL => $queryBuilder->expr()->notIn($column, ':'.$targetParameter),
+ default => null,
+ };
+ }
- return null;
+ protected function configureOptions(OptionsResolver $resolver): void
+ {
+ parent::configureOptions($resolver);
+ $resolver->setDefaults([
+ static::OPT_TARGET_CLASS => null,
+ static::OPT_JSON_SEARCH_URL => null,
+ ]);
+ $resolver->setAllowedTypes(static::OPT_TARGET_CLASS, ['string']);
+ $resolver->setAllowedTypes(static::OPT_JSON_SEARCH_URL, ['string']);
}
}
diff --git a/src/Filter/Type/AjaxRelationFilterType.php b/src/Filter/Type/AjaxRelationFilterType.php
index 29ca4eb..5acfc01 100644
--- a/src/Filter/Type/AjaxRelationFilterType.php
+++ b/src/Filter/Type/AjaxRelationFilterType.php
@@ -4,8 +4,10 @@
namespace araise\TableBundle\Filter\Type;
+use Coduo\ToString\StringConverter;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\PropertyAccess\PropertyAccessor;
class AjaxRelationFilterType extends FilterType
@@ -14,16 +16,16 @@ class AjaxRelationFilterType extends FilterType
public const CRITERIA_NOT_EQUAL = 'not_equal';
+ public const OPT_TARGET_CLASS = 'target_class';
+
+ public const OPT_JSON_SEARCH_URL = 'json_search_url';
+
protected static PropertyAccessor $propertyAccessor;
public function __construct(
- string $column,
- protected string $targetClass,
protected EntityManagerInterface $entityManager,
- protected string $jsonSearchUrl,
- array $joins = []
) {
- parent::__construct($column, $joins);
+ parent::__construct();
}
public function getOperators(): array
@@ -36,14 +38,15 @@ public function getOperators(): array
public function getValueField(?string $value = '0'): string
{
+ $jsonSearchUrl = $this->getOption(static::OPT_JSON_SEARCH_URL);
$field = sprintf(
'';
@@ -53,22 +56,34 @@ public function getValueField(?string $value = '0'): string
public function toDql(string $operator, string $value, string $parameterName, QueryBuilder $queryBuilder)
{
+ $column = $this->getOption(static::OPT_COLUMN);
switch ($operator) {
case static::CRITERIA_EQUAL:
if ((int) $value) {
- return $queryBuilder->expr()->eq($this->getColumn().'.id', (int) $value);
+ return $queryBuilder->expr()->eq($column.'.id', (int) $value);
}
- return $queryBuilder->expr()->isNull($this->getColumn().'.id');
+ return $queryBuilder->expr()->isNull($column.'.id');
case static::CRITERIA_NOT_EQUAL:
if ((int) $value) {
- return $queryBuilder->expr()->neq($this->getColumn().'.id', (int) $value);
+ return $queryBuilder->expr()->neq($column.'.id', (int) $value);
}
- return $queryBuilder->expr()->isNotNull($this->getColumn().'.id');
+ return $queryBuilder->expr()->isNotNull($column.'.id');
}
return null;
}
+
+ protected function configureOptions(OptionsResolver $resolver): void
+ {
+ parent::configureOptions($resolver);
+ $resolver->setDefaults([
+ static::OPT_TARGET_CLASS => null,
+ static::OPT_JSON_SEARCH_URL => null,
+ ]);
+ $resolver->setAllowedTypes(static::OPT_TARGET_CLASS, ['string']);
+ $resolver->setAllowedTypes(static::OPT_JSON_SEARCH_URL, ['string']);
+ }
}
diff --git a/src/Filter/Type/BooleanFilterType.php b/src/Filter/Type/BooleanFilterType.php
index 675784a..c8cc6f3 100644
--- a/src/Filter/Type/BooleanFilterType.php
+++ b/src/Filter/Type/BooleanFilterType.php
@@ -32,14 +32,11 @@ public function getValueField(?string $value = '1'): string
public function toDql(string $operator, string $value, string $parameterName, QueryBuilder $queryBuilder)
{
$value = $value === '1' ? 'true' : 'false';
-
- switch ($operator) {
- case static::CRITERIA_EQUAL:
- return $queryBuilder->expr()->eq($this->getColumn(), $value);
- case static::CRITERIA_NOT_EQUAL:
- return $queryBuilder->expr()->neq($this->getColumn(), $value);
- }
-
- return false;
+ $column = $this->getOption(static::OPT_COLUMN);
+ return match ($operator) {
+ static::CRITERIA_EQUAL => $queryBuilder->expr()->eq($column, $value),
+ static::CRITERIA_NOT_EQUAL => $queryBuilder->expr()->neq($column, $value),
+ default => false,
+ };
}
}
diff --git a/src/Filter/Type/ChoiceFilterType.php b/src/Filter/Type/ChoiceFilterType.php
index 347f886..74eb4f4 100644
--- a/src/Filter/Type/ChoiceFilterType.php
+++ b/src/Filter/Type/ChoiceFilterType.php
@@ -5,6 +5,7 @@
namespace araise\TableBundle\Filter\Type;
use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\OptionsResolver\OptionsResolver;
class ChoiceFilterType extends FilterType
{
@@ -12,13 +13,7 @@ class ChoiceFilterType extends FilterType
public const CRITERIA_NOT_EQUAL = 'not_equal';
- protected array $choices = [];
-
- public function __construct(string $column, array $choices, array $joins = [])
- {
- parent::__construct($column, $joins);
- $this->choices = $choices;
- }
+ public const OPT_CHOICES = 'choices';
public function getOperators(): array
{
@@ -32,7 +27,7 @@ public function getValueField(?string $value = null): string
{
$result = '