Skip to content

Commit

Permalink
feat(filters): are loaded over DI
Browse files Browse the repository at this point in the history
  • Loading branch information
tuxes3 committed Aug 3, 2023
1 parent 0329b46 commit d7cb3f2
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 126 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 17 additions & 0 deletions src/Extension/FilterExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand All @@ -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)
{
Expand All @@ -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
*/
Expand Down
37 changes: 20 additions & 17 deletions src/Filter/FilterGuesser.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -65,7 +67,8 @@ class FilterGuesser
];

public function __construct(
protected EntityManagerInterface $entityManager
protected EntityManagerInterface $entityManager,
protected FilterTypeManager $filterTypeManager,
) {
}

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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();
Expand Down
18 changes: 8 additions & 10 deletions src/Filter/Type/AjaxManyToManyFilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}
}
45 changes: 29 additions & 16 deletions src/Filter/Type/AjaxOneToManyFilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,25 @@

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
{
public const CRITERIA_EQUAL = 'equal';

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
Expand All @@ -58,14 +60,15 @@ public function getOperators(): array

public function getValueField(?string $value = '0'): string
{
$targetClass = $this->getOption(static::OPT_TARGET_CLASS);
$field = sprintf(
'<select name="{name}" class="form-control" data-ajax-select data-ajax-entity="%s">',
$this->targetClass
$targetClass
);

$currentSelection = (int) $value > 0 ? $this->entityManager->getRepository($this->targetClass)->find((int) $value) : null;
$currentSelection = (int) $value > 0 ? $this->entityManager->getRepository($targetClass)->find((int) $value) : null;
if ($currentSelection) {
$field .= sprintf('<option value="%s">%s</option>', $value, $currentSelection->__toString());
$field .= sprintf('<option value="%s">%s</option>', $value, new StringConverter($currentSelection));
}

$field .= '</select>';
Expand All @@ -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']);
}
}
41 changes: 28 additions & 13 deletions src/Filter/Type/AjaxRelationFilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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(
'<select name="{name}" class="form-control" data-araise--core-bundle--select-url-value="%s" data-controller="araise--core-bundle--select" data-araise--core-bundle--select-required-value="0">',
$this->jsonSearchUrl
$jsonSearchUrl
);

$currentSelection = (int) $value > 0 ? $this->entityManager->getRepository($this->targetClass)->find((int) $value) : null;
$targetClass = $this->getOption(static::OPT_TARGET_CLASS);
$currentSelection = (int) $value > 0 ? $this->entityManager->getRepository($targetClass)->find((int) $value) : null;
if ($currentSelection) {
$field .= sprintf('<option value="%s">%s</option>', $value, $currentSelection->__toString());
$field .= sprintf('<option value="%s">%s</option>', $value, new StringConverter($currentSelection));
}

$field .= '</select>';
Expand All @@ -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']);
}
}
15 changes: 6 additions & 9 deletions src/Filter/Type/BooleanFilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
}
}
Loading

0 comments on commit d7cb3f2

Please sign in to comment.