From 1fc0d0ab2f3f14a28d46a88e3483fd948d24d099 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Tue, 24 Sep 2024 16:47:09 +0200 Subject: [PATCH 1/3] Moved Criteria, SortClauses and CriterionMapper --- .../config/services/persistence.yaml | 9 ++ .../Values/Query/AbstractCriterionQuery.php | 111 ++++++++++++++++++ .../Values/Query/AbstractSortClause.php | 59 ++++++++++ .../Criterion/AbstractCompositeCriterion.php | 51 ++++++++ .../Query/Criterion/CriterionInterface.php | 13 ++ .../Query/Criterion/FieldValueCriterion.php | 85 ++++++++++++++ .../Values/Query/Criterion/LogicalAnd.php | 13 ++ .../Values/Query/Criterion/LogicalOr.php | 13 ++ .../Values/Query/CriterionMapperInterface.php | 26 ++++ .../Query/SortClause/FieldValueSortClause.php | 28 +++++ src/contracts/Values/Query/SortDirection.php | 23 ++++ src/lib/Repository/CriterionMapper.php | 50 ++++++++ 12 files changed, 481 insertions(+) create mode 100644 src/bundle/Resources/config/services/persistence.yaml create mode 100644 src/contracts/Values/Query/AbstractCriterionQuery.php create mode 100644 src/contracts/Values/Query/AbstractSortClause.php create mode 100644 src/contracts/Values/Query/Criterion/AbstractCompositeCriterion.php create mode 100644 src/contracts/Values/Query/Criterion/CriterionInterface.php create mode 100644 src/contracts/Values/Query/Criterion/FieldValueCriterion.php create mode 100644 src/contracts/Values/Query/Criterion/LogicalAnd.php create mode 100644 src/contracts/Values/Query/Criterion/LogicalOr.php create mode 100644 src/contracts/Values/Query/CriterionMapperInterface.php create mode 100644 src/contracts/Values/Query/SortClause/FieldValueSortClause.php create mode 100644 src/contracts/Values/Query/SortDirection.php create mode 100644 src/lib/Repository/CriterionMapper.php diff --git a/src/bundle/Resources/config/services/persistence.yaml b/src/bundle/Resources/config/services/persistence.yaml new file mode 100644 index 0000000..ddab7d2 --- /dev/null +++ b/src/bundle/Resources/config/services/persistence.yaml @@ -0,0 +1,9 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\CoreSearch\Repository\CriterionMapper: + arguments: + $mappers: !tagged_iterator ibexa.core_search.criterion_mapper diff --git a/src/contracts/Values/Query/AbstractCriterionQuery.php b/src/contracts/Values/Query/AbstractCriterionQuery.php new file mode 100644 index 0000000..34c24dc --- /dev/null +++ b/src/contracts/Values/Query/AbstractCriterionQuery.php @@ -0,0 +1,111 @@ +query = $query; + $this->sortClauses = $sortClauses ?? []; + $this->offset = $offset; + $this->limit = $limit; + } + + /** + * @param TCriterion|null $criterion + */ + final public function setQuery(?CriterionInterface $criterion): void + { + $this->query = $criterion; + } + + /** + * @return TCriterion|null + */ + final public function getQuery(): ?CriterionInterface + { + return $this->query; + } + + final public function hasQuery(): bool + { + return $this->query !== null; + } + + final public function getOffset(): int + { + return $this->offset; + } + + final public function setOffset(int $offset): void + { + $this->offset = $offset; + } + + final public function getLimit(): ?int + { + return $this->limit; + } + + final public function setLimit(?int $limit): void + { + $this->limit = $limit; + } + + /** + * @return TSortClause[] + */ + final public function getSortClauses(): array + { + return $this->sortClauses; + } + + /** + * @phpstan-param TSortClause $sortClause + */ + final public function addSortClause(AbstractSortClause $sortClause): void + { + $this->sortClauses[] = $sortClause; + } + + /** + * @phpstan-param TSortClause[] $sortClauses + */ + final public function setSortClauses(array $sortClauses): void + { + $this->sortClauses = $sortClauses; + } +} diff --git a/src/contracts/Values/Query/AbstractSortClause.php b/src/contracts/Values/Query/AbstractSortClause.php new file mode 100644 index 0000000..2728e93 --- /dev/null +++ b/src/contracts/Values/Query/AbstractSortClause.php @@ -0,0 +1,59 @@ +setDirection($sortDirection); + } + + final public function getDirection(): string + { + return $this->direction; + } + + /** + * @throws \InvalidArgumentException if the given sort direction is invalid + */ + final public function setDirection(string $direction): void + { + if (!SortDirection::isValid($direction)) { + throw new InvalidArgumentException(sprintf( + 'Sort direction must be one of %1$s::ASC or %1$s::DESC', + SortDirection::class, + )); + } + + /** @var SortDirection::* $direction */ + $this->direction = $direction; + } +} diff --git a/src/contracts/Values/Query/Criterion/AbstractCompositeCriterion.php b/src/contracts/Values/Query/Criterion/AbstractCompositeCriterion.php new file mode 100644 index 0000000..bd20578 --- /dev/null +++ b/src/contracts/Values/Query/Criterion/AbstractCompositeCriterion.php @@ -0,0 +1,51 @@ + */ + private array $criteria; + + public function __construct(CriterionInterface ...$criteria) + { + $this->criteria = $criteria; + } + + public function add(CriterionInterface ...$criteria): void + { + $this->setCriteria( + ...$this->criteria, + ...$criteria, + ); + } + + public function remove(CriterionInterface ...$criteria): void + { + $this->setCriteria(...array_filter( + $this->criteria, + static function (CriterionInterface $criterion) use ($criteria): bool { + return !in_array($criterion, $criteria, true); + }, + )); + } + + public function setCriteria(CriterionInterface ...$criteria): void + { + $this->criteria = $criteria; + } + + /** + * @return array<\Ibexa\Contracts\CoreSearch\Values\Query\Criterion\CriterionInterface> + */ + final public function getCriteria(): array + { + return $this->criteria; + } +} diff --git a/src/contracts/Values/Query/Criterion/CriterionInterface.php b/src/contracts/Values/Query/Criterion/CriterionInterface.php new file mode 100644 index 0000000..9b57485 --- /dev/null +++ b/src/contracts/Values/Query/Criterion/CriterionInterface.php @@ -0,0 +1,13 @@ +'; + /** @final */ + public const COMPARISON_LT = '<'; + /** @final */ + public const COMPARISON_LTE = '<='; + /** @final */ + public const COMPARISON_GT = '>'; + /** @final */ + public const COMPARISON_GTE = '>='; + /** @final */ + public const COMPARISON_IN = 'IN'; + /** @final */ + public const COMPARISON_NIN = 'NIN'; + /** @final */ + public const COMPARISON_CONTAINS = 'CONTAINS'; + /** @final */ + public const COMPARISON_MEMBER_OF = 'MEMBER_OF'; + /** @final */ + public const COMPARISON_STARTS_WITH = 'STARTS_WITH'; + /** @final */ + public const COMPARISON_ENDS_WITH = 'ENDS_WITH'; + + private string $field; + + /** @var mixed */ + private $value; + + private string $operator; + + /** + * @param mixed $value + */ + public function __construct(string $field, $value, ?string $operator = null) + { + $this->field = $field; + $this->value = $value; + $this->operator = $operator ?? (is_array($value) ? self::COMPARISON_IN : self::COMPARISON_EQ); + } + + public function getField(): string + { + return $this->field; + } + + /** + * @param mixed $value + */ + public function setValue($value): void + { + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + public function setOperator(string $operator): void + { + $this->operator = $operator; + } + + public function getOperator(): string + { + return $this->operator; + } +} diff --git a/src/contracts/Values/Query/Criterion/LogicalAnd.php b/src/contracts/Values/Query/Criterion/LogicalAnd.php new file mode 100644 index 0000000..b950ac6 --- /dev/null +++ b/src/contracts/Values/Query/Criterion/LogicalAnd.php @@ -0,0 +1,13 @@ +field = $field; + } + + public function getField(): string + { + return $this->field; + } +} diff --git a/src/contracts/Values/Query/SortDirection.php b/src/contracts/Values/Query/SortDirection.php new file mode 100644 index 0000000..90f9c6c --- /dev/null +++ b/src/contracts/Values/Query/SortDirection.php @@ -0,0 +1,23 @@ +> + */ + private iterable $mappers; + + /** + * @phpstan-param iterable<\Ibexa\Contracts\CoreSearch\Values\Query\CriterionMapperInterface< + * \Ibexa\Contracts\CoreSearch\Values\Query\Criterion\CriterionInterface, + * >> $mappers + */ + public function __construct(iterable $mappers) + { + $this->mappers = $mappers; + } + + public function handle(CriterionInterface $criterion): Expression + { + foreach ($this->mappers as $mapper) { + if ($mapper->canHandle($criterion)) { + return $mapper->handle($criterion, $this); + } + } + + throw new LogicException(sprintf( + 'Unable to handle "%s" criterion.', + get_class($criterion), + )); + } +} From db8e10367fdac2bf726f531c556dbcfc115ba432 Mon Sep 17 00:00:00 2001 From: mikolaj Date: Thu, 26 Sep 2024 14:58:02 +0200 Subject: [PATCH 2/3] Fixed issue reported by Deptrac --- src/bundle/Resources/config/services/persistence.yaml | 2 +- .../Repository => contracts/Values/Query}/CriterionMapper.php | 2 +- src/contracts/Values/Query/CriterionMapperInterface.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) rename src/{lib/Repository => contracts/Values/Query}/CriterionMapper.php (96%) diff --git a/src/bundle/Resources/config/services/persistence.yaml b/src/bundle/Resources/config/services/persistence.yaml index ddab7d2..f4f5313 100644 --- a/src/bundle/Resources/config/services/persistence.yaml +++ b/src/bundle/Resources/config/services/persistence.yaml @@ -4,6 +4,6 @@ services: autoconfigure: true public: false - Ibexa\CoreSearch\Repository\CriterionMapper: + Ibexa\Contracts\CoreSearch\Values\Query\CriterionMapper: arguments: $mappers: !tagged_iterator ibexa.core_search.criterion_mapper diff --git a/src/lib/Repository/CriterionMapper.php b/src/contracts/Values/Query/CriterionMapper.php similarity index 96% rename from src/lib/Repository/CriterionMapper.php rename to src/contracts/Values/Query/CriterionMapper.php index b678965..46cadba 100644 --- a/src/lib/Repository/CriterionMapper.php +++ b/src/contracts/Values/Query/CriterionMapper.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace Ibexa\CoreSearch\Repository; +namespace Ibexa\Contracts\CoreSearch\Values\Query; use Doctrine\Common\Collections\Expr\Expression; use Ibexa\Contracts\CoreSearch\Values\Query\Criterion\CriterionInterface; diff --git a/src/contracts/Values/Query/CriterionMapperInterface.php b/src/contracts/Values/Query/CriterionMapperInterface.php index 1fad365..e828956 100644 --- a/src/contracts/Values/Query/CriterionMapperInterface.php +++ b/src/contracts/Values/Query/CriterionMapperInterface.php @@ -10,7 +10,6 @@ use Doctrine\Common\Collections\Expr\Expression; use Ibexa\Contracts\CoreSearch\Values\Query\Criterion\CriterionInterface; -use Ibexa\CoreSearch\Repository\CriterionMapper; /** * @template C of \Ibexa\Contracts\CoreSearch\Values\Query\Criterion\CriterionInterface From 53d77ffb76f90a72b902eb7b923f11dc74acc7ea Mon Sep 17 00:00:00 2001 From: mikolaj Date: Thu, 26 Sep 2024 15:29:29 +0200 Subject: [PATCH 3/3] CR --- .../Resources/config/services/{persistence.yaml => query.yaml} | 0 src/contracts/Values/Query/Criterion/FieldValueCriterion.php | 2 +- src/contracts/Values/Query/Criterion/LogicalAnd.php | 2 +- src/contracts/Values/Query/Criterion/LogicalOr.php | 2 +- src/contracts/Values/Query/SortClause/FieldValueSortClause.php | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/bundle/Resources/config/services/{persistence.yaml => query.yaml} (100%) diff --git a/src/bundle/Resources/config/services/persistence.yaml b/src/bundle/Resources/config/services/query.yaml similarity index 100% rename from src/bundle/Resources/config/services/persistence.yaml rename to src/bundle/Resources/config/services/query.yaml diff --git a/src/contracts/Values/Query/Criterion/FieldValueCriterion.php b/src/contracts/Values/Query/Criterion/FieldValueCriterion.php index 812396c..0337a1a 100644 --- a/src/contracts/Values/Query/Criterion/FieldValueCriterion.php +++ b/src/contracts/Values/Query/Criterion/FieldValueCriterion.php @@ -8,7 +8,7 @@ namespace Ibexa\Contracts\CoreSearch\Values\Query\Criterion; -class FieldValueCriterion implements CriterionInterface +final class FieldValueCriterion implements CriterionInterface { /** @final */ public const COMPARISON_EQ = '='; diff --git a/src/contracts/Values/Query/Criterion/LogicalAnd.php b/src/contracts/Values/Query/Criterion/LogicalAnd.php index b950ac6..3dd6644 100644 --- a/src/contracts/Values/Query/Criterion/LogicalAnd.php +++ b/src/contracts/Values/Query/Criterion/LogicalAnd.php @@ -8,6 +8,6 @@ namespace Ibexa\Contracts\CoreSearch\Values\Query\Criterion; -class LogicalAnd extends AbstractCompositeCriterion +final class LogicalAnd extends AbstractCompositeCriterion { } diff --git a/src/contracts/Values/Query/Criterion/LogicalOr.php b/src/contracts/Values/Query/Criterion/LogicalOr.php index db044c1..f5c8f10 100644 --- a/src/contracts/Values/Query/Criterion/LogicalOr.php +++ b/src/contracts/Values/Query/Criterion/LogicalOr.php @@ -8,6 +8,6 @@ namespace Ibexa\Contracts\CoreSearch\Values\Query\Criterion; -class LogicalOr extends AbstractCompositeCriterion +final class LogicalOr extends AbstractCompositeCriterion { } diff --git a/src/contracts/Values/Query/SortClause/FieldValueSortClause.php b/src/contracts/Values/Query/SortClause/FieldValueSortClause.php index 1e797d9..c249193 100644 --- a/src/contracts/Values/Query/SortClause/FieldValueSortClause.php +++ b/src/contracts/Values/Query/SortClause/FieldValueSortClause.php @@ -10,7 +10,7 @@ use Ibexa\Contracts\CoreSearch\Values\Query\AbstractSortClause; -class FieldValueSortClause extends AbstractSortClause +final class FieldValueSortClause extends AbstractSortClause { private string $field;