From 8708d4a0fd36f054db591b3b62ae50a77a4c4905 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 12:30:06 +0300 Subject: [PATCH 01/16] all, any --- src/Filter/All.php | 15 ------ src/Filter/Any.php | 15 ------ src/Filter/EqualsEmpty.php | 1 + src/Filter/GroupFilter.php | 80 --------------------------- src/FilterHandler/AllHandler.php | 4 +- src/FilterHandler/AnyHandler.php | 4 +- src/FilterHandler/GroupHandler.php | 86 ++++++++++++++++++++++++++++++ tests/.gitkeep | 0 tests/FiltersTest.php | 14 ++--- tests/QueryWithFiltersTest.php | 4 +- 10 files changed, 100 insertions(+), 123 deletions(-) delete mode 100644 src/Filter/All.php delete mode 100644 src/Filter/Any.php delete mode 100644 src/Filter/GroupFilter.php create mode 100644 src/FilterHandler/GroupHandler.php delete mode 100644 tests/.gitkeep diff --git a/src/Filter/All.php b/src/Filter/All.php deleted file mode 100644 index cb45700..0000000 --- a/src/Filter/All.php +++ /dev/null @@ -1,15 +0,0 @@ -filters = $filters; - } - - public function toCriteriaArray(): array - { - $array = [static::getOperator()]; - - foreach ($this->filters as $filter) { - if ($filter instanceof FilterInterface) { - $arr = $filter->toCriteriaArray(); - } elseif (is_array($filter)) { - $arr = $filter; - } else { - throw new RuntimeException( - sprintf( - '$filter must be instance of "%s" or array. %s given.', - FilterInterface::class, - get_debug_type($filter) - ) - ); - } - - if ($arr !== []) { - $array[] = $arr; - } - } - - return count($array) > 1 ? $array : []; - } - - public function withCriteriaArray(array $criteriaArray): static - { - return static::fromCriteriaArray($criteriaArray); - } - - public static function fromCriteriaArray(array $criteriaArray): static - { - foreach ($criteriaArray as $key => $criteria) { - if (!is_array($criteria)) { - throw new InvalidArgumentException(sprintf('Invalid filter on "%s" key.', $key)); - } - - $operator = array_shift($criteria); - - if (!is_string($operator) || $operator === '') { - throw new InvalidArgumentException(sprintf('Invalid filter operator on "%s" key.', $key)); - } - } - - $filter = new static(); - $filter->filters = $criteriaArray; - - return $filter; - } -} diff --git a/src/FilterHandler/AllHandler.php b/src/FilterHandler/AllHandler.php index 0264b31..3f31b01 100644 --- a/src/FilterHandler/AllHandler.php +++ b/src/FilterHandler/AllHandler.php @@ -4,9 +4,9 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Db\Filter\All; +use Yiisoft\Data\Reader\Filter\All; -final class AllHandler extends AbstractHandler +final class AllHandler extends GroupHandler { public function getOperator(): string { diff --git a/src/FilterHandler/AnyHandler.php b/src/FilterHandler/AnyHandler.php index 434a13d..e8ac314 100644 --- a/src/FilterHandler/AnyHandler.php +++ b/src/FilterHandler/AnyHandler.php @@ -4,9 +4,9 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Db\Filter\Any; +use Yiisoft\Data\Reader\Filter\Any; -final class AnyHandler extends AbstractHandler +final class AnyHandler extends GroupHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/GroupHandler.php new file mode 100644 index 0000000..8694057 --- /dev/null +++ b/src/FilterHandler/GroupHandler.php @@ -0,0 +1,86 @@ +prepareCondition($filter->toCriteriaArray()); + + if ($condition === null) { + return $query; + } + + return $query->andWhere($condition); + } + + public function applyHaving(QueryInterface $query, FilterInterface $having): QueryInterface + { + $condition = $this->prepareCondition($having->toCriteriaArray()); + + if ($condition === null) { + return $query; + } + + return $query->andHaving($condition); + } + + private function prepareCondition(array $criteria): ?array + { + if (!isset($criteria[0])) { + throw new LogicException('Incorrect criteria array.'); + } + + switch ($criteria[0]) { + case 'and': + case 'or': + /** @psalm-var string $criteria[0] */ + if (!array_key_exists(1, $criteria)) { + throw new LogicException( + sprintf( + 'Not found second parameter for the "%s" operator.', + $criteria[0], + ) + ); + } + if (!is_array($criteria[1])) { + throw new LogicException( + sprintf( + 'The second parameter for "%s" operator must be an array. Got %s.', + $criteria[0], + get_debug_type($criteria[1]) + ) + ); + } + if (empty($criteria[1])) { + return null; + } + $condition = [$criteria[0]]; + foreach ($criteria[1] as $subCriteria) { + if (!is_array($subCriteria)) { + throw new LogicException('Incorrect sub-criteria.'); + } + $condition[] = $this->prepareCondition($subCriteria); + } + return $condition; + + case 'like': + if (array_keys($criteria) !== [0, 1, 2] || !is_string($criteria[1]) || !is_string($criteria[2])) { + throw new LogicException('Incorrect criteria for the "like" operator.'); + } + return ['like', $criteria[1], $criteria[2]]; + } + + throw new LogicException(sprintf('Not supported operator: %s.', (string) $criteria[0])); + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/FiltersTest.php b/tests/FiltersTest.php index 8a138f6..e38f2f8 100644 --- a/tests/FiltersTest.php +++ b/tests/FiltersTest.php @@ -6,8 +6,6 @@ use DateTime; use PHPUnit\Framework\TestCase; -use Yiisoft\Data\Db\Filter\All as FilterAll; -use Yiisoft\Data\Db\Filter\Any as FilterAny; use Yiisoft\Data\Db\Filter\Between; use Yiisoft\Data\Db\Filter\CompareFilter; use Yiisoft\Data\Db\Filter\Equals; @@ -22,6 +20,8 @@ use Yiisoft\Data\Db\Filter\Like; use Yiisoft\Data\Db\Filter\Not; use Yiisoft\Data\Db\Filter\NotEquals; +use Yiisoft\Data\Reader\Filter\All; +use Yiisoft\Data\Reader\Filter\Any; use Yiisoft\Data\Reader\FilterInterface; use function mb_strtoupper; @@ -201,20 +201,20 @@ public function groupDataProvider(): array return [ //AND [ - new FilterAll(...$filters), + new All(...$filters), array_merge(['and'], $map), ], [ - new FilterAll(...$nullMap), + new All(...$nullMap), [], ], //OR [ - new FilterAny(...$filters), + new Any(...$filters), array_merge(['or'], $map), ], [ - new FilterAny(...$nullMap), + new Any(...$nullMap), [], ], ]; @@ -295,7 +295,7 @@ public function testNotFilter(FilterInterface $filter, array $expected): void /** * @dataProvider groupDataProvider */ - public function testGroupFilters(GroupFilter $filter, array $expected): void + public function testGroupFilters(All|Any $filter, array $expected): void { $this->assertSame($expected, $filter->toCriteriaArray()); } diff --git a/tests/QueryWithFiltersTest.php b/tests/QueryWithFiltersTest.php index ee20c6e..1a4f275 100644 --- a/tests/QueryWithFiltersTest.php +++ b/tests/QueryWithFiltersTest.php @@ -6,8 +6,6 @@ use DateTime; use PHPUnit\Framework\TestCase; -use Yiisoft\Data\Db\Filter\All; -use Yiisoft\Data\Db\Filter\Any; use Yiisoft\Data\Db\Filter\Between; use Yiisoft\Data\Db\Filter\CompareFilter; use Yiisoft\Data\Db\Filter\Equals; @@ -24,6 +22,8 @@ use Yiisoft\Data\Db\Filter\OrLike; use Yiisoft\Data\Db\QueryDataReader; use Yiisoft\Data\Db\Tests\Support\TestTrait; +use Yiisoft\Data\Reader\Filter\All; +use Yiisoft\Data\Reader\Filter\Any; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Query\Query; From 891e48c13550af0dd869b5e590958eddbd933957 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 15 Feb 2024 09:30:43 +0000 Subject: [PATCH 02/16] Apply fixes from StyleCI --- tests/FiltersTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/FiltersTest.php b/tests/FiltersTest.php index e38f2f8..51700db 100644 --- a/tests/FiltersTest.php +++ b/tests/FiltersTest.php @@ -12,7 +12,6 @@ use Yiisoft\Data\Db\Filter\EqualsEmpty; use Yiisoft\Data\Db\Filter\GreaterThan; use Yiisoft\Data\Db\Filter\GreaterThanOrEqual; -use Yiisoft\Data\Db\Filter\GroupFilter; use Yiisoft\Data\Db\Filter\ILike; use Yiisoft\Data\Db\Filter\In; use Yiisoft\Data\Db\Filter\LessThan; From e86142c5b385da3d89ebf28b8871e60d43f380b0 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 14:43:07 +0300 Subject: [PATCH 03/16] refactor --- src/FilterHandler/AllHandler.php | 2 +- src/FilterHandler/AnyHandler.php | 2 +- .../{AbstractHandler.php => BaseHandler.php} | 14 ++++----- src/FilterHandler/BetweenHandler.php | 2 +- ...{GroupHandler.php => ConditionFactory.php} | 30 ++----------------- src/FilterHandler/EqualsEmptyHandler.php | 5 +--- src/FilterHandler/EqualsHandler.php | 2 +- src/FilterHandler/ExistsHandler.php | 2 +- src/FilterHandler/GreaterThanHandler.php | 2 +- .../GreaterThanOrEqualHandler.php | 2 +- src/FilterHandler/ILikeHandler.php | 2 +- src/FilterHandler/InHandler.php | 2 +- src/FilterHandler/IsNullHandler.php | 2 +- src/FilterHandler/LessThanHandler.php | 2 +- src/FilterHandler/LessThanOrEqualHandler.php | 2 +- src/FilterHandler/LikeHandler.php | 2 +- src/FilterHandler/NotEqualsHandler.php | 2 +- src/FilterHandler/NotHandler.php | 2 +- src/FilterHandler/OrILikeHandler.php | 2 +- src/FilterHandler/OrLikeHandler.php | 2 +- tests/QueryWithFiltersTest.php | 6 ++-- 21 files changed, 31 insertions(+), 58 deletions(-) rename src/FilterHandler/{AbstractHandler.php => BaseHandler.php} (55%) rename src/FilterHandler/{GroupHandler.php => ConditionFactory.php} (68%) diff --git a/src/FilterHandler/AllHandler.php b/src/FilterHandler/AllHandler.php index 3f31b01..8e2fbf7 100644 --- a/src/FilterHandler/AllHandler.php +++ b/src/FilterHandler/AllHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\All; -final class AllHandler extends GroupHandler +final class AllHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/AnyHandler.php b/src/FilterHandler/AnyHandler.php index e8ac314..f705b09 100644 --- a/src/FilterHandler/AnyHandler.php +++ b/src/FilterHandler/AnyHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\Any; -final class AnyHandler extends GroupHandler +final class AnyHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/AbstractHandler.php b/src/FilterHandler/BaseHandler.php similarity index 55% rename from src/FilterHandler/AbstractHandler.php rename to src/FilterHandler/BaseHandler.php index 7d37a14..5bcedf0 100644 --- a/src/FilterHandler/AbstractHandler.php +++ b/src/FilterHandler/BaseHandler.php @@ -7,27 +7,27 @@ use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Db\Query\QueryInterface; -abstract class AbstractHandler implements QueryHandlerInterface +abstract class BaseHandler implements QueryHandlerInterface { public function applyFilter(QueryInterface $query, FilterInterface $filter): QueryInterface { - $array = $filter->toCriteriaArray(); + $condition = ConditionFactory::make($filter->toCriteriaArray()); - if ($array === []) { + if ($condition === null) { return $query; } - return $query->andWhere($array); + return $query->andWhere($condition); } public function applyHaving(QueryInterface $query, FilterInterface $having): QueryInterface { - $array = $having->toCriteriaArray(); + $condition = ConditionFactory::make($having->toCriteriaArray()); - if ($array === []) { + if ($condition === null) { return $query; } - return $query->andHaving($array); + return $query->andHaving($condition); } } diff --git a/src/FilterHandler/BetweenHandler.php b/src/FilterHandler/BetweenHandler.php index fefc196..d9ddebb 100644 --- a/src/FilterHandler/BetweenHandler.php +++ b/src/FilterHandler/BetweenHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\Between; -final class BetweenHandler extends AbstractHandler +final class BetweenHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/ConditionFactory.php similarity index 68% rename from src/FilterHandler/GroupHandler.php rename to src/FilterHandler/ConditionFactory.php index 8694057..078a758 100644 --- a/src/FilterHandler/GroupHandler.php +++ b/src/FilterHandler/ConditionFactory.php @@ -5,37 +5,13 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Reader\FilterInterface; -use Yiisoft\Db\Query\QueryInterface; /** * @internal */ -abstract class GroupHandler implements QueryHandlerInterface +final class ConditionFactory { - public function applyFilter(QueryInterface $query, FilterInterface $filter): QueryInterface - { - $condition = $this->prepareCondition($filter->toCriteriaArray()); - - if ($condition === null) { - return $query; - } - - return $query->andWhere($condition); - } - - public function applyHaving(QueryInterface $query, FilterInterface $having): QueryInterface - { - $condition = $this->prepareCondition($having->toCriteriaArray()); - - if ($condition === null) { - return $query; - } - - return $query->andHaving($condition); - } - - private function prepareCondition(array $criteria): ?array + public static function make(array $criteria): ?array { if (!isset($criteria[0])) { throw new LogicException('Incorrect criteria array.'); @@ -70,7 +46,7 @@ private function prepareCondition(array $criteria): ?array if (!is_array($subCriteria)) { throw new LogicException('Incorrect sub-criteria.'); } - $condition[] = $this->prepareCondition($subCriteria); + $condition[] = self::make($subCriteria); } return $condition; diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index 496a2cd..824c733 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -6,11 +6,8 @@ use Yiisoft\Data\Db\Filter\EqualsEmpty; -final class EqualsEmptyHandler extends AbstractHandler +final class EqualsEmptyHandler extends BaseHandler { - /** - * @inheritDoc - */ public function getOperator(): string { return EqualsEmpty::getOperator(); diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index 62b572c..8acbc95 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\Equals; -final class EqualsHandler extends AbstractHandler +final class EqualsHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/ExistsHandler.php b/src/FilterHandler/ExistsHandler.php index 23dd94e..d72b59c 100644 --- a/src/FilterHandler/ExistsHandler.php +++ b/src/FilterHandler/ExistsHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\Exists; -final class ExistsHandler extends AbstractHandler +final class ExistsHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GreaterThanHandler.php b/src/FilterHandler/GreaterThanHandler.php index 8bf4067..1a50546 100644 --- a/src/FilterHandler/GreaterThanHandler.php +++ b/src/FilterHandler/GreaterThanHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\GreaterThan; -final class GreaterThanHandler extends AbstractHandler +final class GreaterThanHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GreaterThanOrEqualHandler.php b/src/FilterHandler/GreaterThanOrEqualHandler.php index 6d31118..c6f2822 100644 --- a/src/FilterHandler/GreaterThanOrEqualHandler.php +++ b/src/FilterHandler/GreaterThanOrEqualHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\GreaterThanOrEqual; -final class GreaterThanOrEqualHandler extends AbstractHandler +final class GreaterThanOrEqualHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/ILikeHandler.php b/src/FilterHandler/ILikeHandler.php index fbf9a8f..2af3d1c 100644 --- a/src/FilterHandler/ILikeHandler.php +++ b/src/FilterHandler/ILikeHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\ILike; -final class ILikeHandler extends AbstractHandler +final class ILikeHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/InHandler.php b/src/FilterHandler/InHandler.php index 229ab78..ff2083a 100644 --- a/src/FilterHandler/InHandler.php +++ b/src/FilterHandler/InHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\In; -final class InHandler extends AbstractHandler +final class InHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/IsNullHandler.php b/src/FilterHandler/IsNullHandler.php index 29c775c..e8a9323 100644 --- a/src/FilterHandler/IsNullHandler.php +++ b/src/FilterHandler/IsNullHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\IsNull; -final class IsNullHandler extends AbstractHandler +final class IsNullHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/LessThanHandler.php b/src/FilterHandler/LessThanHandler.php index 65e8d3b..c20187f 100644 --- a/src/FilterHandler/LessThanHandler.php +++ b/src/FilterHandler/LessThanHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\LessThan; -final class LessThanHandler extends AbstractHandler +final class LessThanHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/LessThanOrEqualHandler.php b/src/FilterHandler/LessThanOrEqualHandler.php index a37a345..7c4e020 100644 --- a/src/FilterHandler/LessThanOrEqualHandler.php +++ b/src/FilterHandler/LessThanOrEqualHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\LessThanOrEqual; -final class LessThanOrEqualHandler extends AbstractHandler +final class LessThanOrEqualHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/LikeHandler.php b/src/FilterHandler/LikeHandler.php index e9a6c44..6d96162 100644 --- a/src/FilterHandler/LikeHandler.php +++ b/src/FilterHandler/LikeHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\Like; -final class LikeHandler extends AbstractHandler +final class LikeHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/NotEqualsHandler.php b/src/FilterHandler/NotEqualsHandler.php index eaa5edd..2961b65 100644 --- a/src/FilterHandler/NotEqualsHandler.php +++ b/src/FilterHandler/NotEqualsHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\NotEquals; -final class NotEqualsHandler extends AbstractHandler +final class NotEqualsHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/NotHandler.php b/src/FilterHandler/NotHandler.php index 3411646..03f8d57 100644 --- a/src/FilterHandler/NotHandler.php +++ b/src/FilterHandler/NotHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\Not; -final class NotHandler extends AbstractHandler +final class NotHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/OrILikeHandler.php b/src/FilterHandler/OrILikeHandler.php index 8de37e4..e71462c 100644 --- a/src/FilterHandler/OrILikeHandler.php +++ b/src/FilterHandler/OrILikeHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\OrILike; -final class OrILikeHandler extends AbstractHandler +final class OrILikeHandler extends BaseHandler { public function getOperator(): string { diff --git a/src/FilterHandler/OrLikeHandler.php b/src/FilterHandler/OrLikeHandler.php index b018274..65b9dbd 100644 --- a/src/FilterHandler/OrLikeHandler.php +++ b/src/FilterHandler/OrLikeHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Db\Filter\OrLike; -final class OrLikeHandler extends AbstractHandler +final class OrLikeHandler extends BaseHandler { public function getOperator(): string { diff --git a/tests/QueryWithFiltersTest.php b/tests/QueryWithFiltersTest.php index 1a4f275..6d47bb6 100644 --- a/tests/QueryWithFiltersTest.php +++ b/tests/QueryWithFiltersTest.php @@ -196,7 +196,7 @@ public function groupFilterDataProvider(): array "([greater_than] > 15) OR ([less_than_or_equal] <= 10) OR ([not_equals] != 'test') OR (([id] = 8) AND ([name] LIKE '%bar%'))", ], [ - All::fromCriteriaArray([ + (new All())->withCriteriaArray([ ['>', 'id', 88], [ 'or', @@ -207,7 +207,7 @@ public function groupFilterDataProvider(): array "([id] > 88) AND (([state] = 2) OR ([name] LIKE '%eva%'))", ], [ - Any::fromCriteriaArray([ + (new All())->withCriteriaArray([ ['>', 'id', 88], [ 'and', @@ -245,7 +245,7 @@ public function groupFilterDataProvider(): array /** * @dataProvider groupFilterDataProvider */ - public function testGroupFilter(GroupFilter $filter, string $expected): void + public function testGroupFilter(All|Any $filter, string $expected): void { $db = $this->getConnection(); $query = (new Query($db)) From 9481e7cbfaf29711a60c8c9a1e10c5720a9310d3 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:03:33 +0300 Subject: [PATCH 04/16] improve --- src/AbstractQueryDataReader.php | 12 +- src/ColumnFormatterTrait.php | 31 -- src/Filter/Between.php | 68 ---- src/Filter/CompareFilter.php | 72 ---- src/Filter/Equals.php | 25 -- src/Filter/EqualsEmpty.php | 74 ---- src/Filter/GreaterThan.php | 15 - src/Filter/GreaterThanOrEqual.php | 15 - src/Filter/ILike.php | 13 - src/Filter/In.php | 27 -- src/Filter/IsNull.php | 30 -- src/Filter/LessThan.php | 15 - src/Filter/LessThanOrEqual.php | 15 - src/Filter/Like.php | 15 - src/Filter/MatchFilter.php | 108 ------ src/Filter/Not.php | 79 ---- src/Filter/NotEquals.php | 30 -- src/Filter/OrILike.php | 13 - src/Filter/OrLike.php | 13 - src/FilterHandler/BetweenHandler.php | 2 +- src/FilterHandler/ConditionFactory.php | 254 ++++++++++--- src/FilterHandler/EqualsEmptyHandler.php | 2 +- src/FilterHandler/EqualsHandler.php | 2 +- src/FilterHandler/EqualsNullHandler.php | 15 + src/FilterHandler/GreaterThanHandler.php | 2 +- .../GreaterThanOrEqualHandler.php | 2 +- src/FilterHandler/ILikeHandler.php | 15 - src/FilterHandler/InHandler.php | 2 +- src/FilterHandler/IsNullHandler.php | 15 - src/FilterHandler/LessThanHandler.php | 2 +- src/FilterHandler/LessThanOrEqualHandler.php | 2 +- src/FilterHandler/LikeHandler.php | 2 +- src/FilterHandler/NotEqualsHandler.php | 15 - src/FilterHandler/NotHandler.php | 2 +- src/FilterHandler/OrILikeHandler.php | 15 - src/FilterHandler/OrLikeHandler.php | 15 - tests/DataFilterTest.php | 63 ++-- tests/DataReaderTest.php | 30 +- tests/FiltersTest.php | 340 ------------------ tests/QueryWithFiltersTest.php | 125 +++---- 40 files changed, 332 insertions(+), 1260 deletions(-) delete mode 100644 src/ColumnFormatterTrait.php delete mode 100644 src/Filter/Between.php delete mode 100644 src/Filter/CompareFilter.php delete mode 100644 src/Filter/Equals.php delete mode 100644 src/Filter/EqualsEmpty.php delete mode 100644 src/Filter/GreaterThan.php delete mode 100644 src/Filter/GreaterThanOrEqual.php delete mode 100644 src/Filter/ILike.php delete mode 100644 src/Filter/In.php delete mode 100644 src/Filter/IsNull.php delete mode 100644 src/Filter/LessThan.php delete mode 100644 src/Filter/LessThanOrEqual.php delete mode 100644 src/Filter/Like.php delete mode 100644 src/Filter/MatchFilter.php delete mode 100644 src/Filter/Not.php delete mode 100644 src/Filter/NotEquals.php delete mode 100644 src/Filter/OrILike.php delete mode 100644 src/Filter/OrLike.php create mode 100644 src/FilterHandler/EqualsNullHandler.php delete mode 100644 src/FilterHandler/ILikeHandler.php delete mode 100644 src/FilterHandler/IsNullHandler.php delete mode 100644 src/FilterHandler/NotEqualsHandler.php delete mode 100644 src/FilterHandler/OrILikeHandler.php delete mode 100644 src/FilterHandler/OrLikeHandler.php delete mode 100644 tests/FiltersTest.php diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index 80e630e..653d9ae 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -15,16 +15,12 @@ use Yiisoft\Data\Db\FilterHandler\ExistsHandler; use Yiisoft\Data\Db\FilterHandler\GreaterThanHandler; use Yiisoft\Data\Db\FilterHandler\GreaterThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\ILikeHandler; use Yiisoft\Data\Db\FilterHandler\InHandler; -use Yiisoft\Data\Db\FilterHandler\IsNullHandler; +use Yiisoft\Data\Db\FilterHandler\EqualsNullHandler; use Yiisoft\Data\Db\FilterHandler\LessThanHandler; use Yiisoft\Data\Db\FilterHandler\LessThanOrEqualHandler; use Yiisoft\Data\Db\FilterHandler\LikeHandler; -use Yiisoft\Data\Db\FilterHandler\NotEqualsHandler; use Yiisoft\Data\Db\FilterHandler\NotHandler; -use Yiisoft\Data\Db\FilterHandler\OrILikeHandler; -use Yiisoft\Data\Db\FilterHandler\OrLikeHandler; use Yiisoft\Data\Db\FilterHandler\QueryHandlerInterface; use Yiisoft\Data\Reader\FilterHandlerInterface; use Yiisoft\Data\Reader\FilterInterface; @@ -80,15 +76,11 @@ public function __construct(private QueryInterface $query) new LessThanHandler(), new LessThanOrEqualHandler(), new LikeHandler(), - new ILikeHandler(), - new OrLikeHandler(), - new OrILikeHandler(), new InHandler(), new ExistsHandler(), - new NotEqualsHandler(), new NotHandler(), new BetweenHandler(), - new IsNullHandler(), + new EqualsNullHandler(), new EqualsEmptyHandler() ); } diff --git a/src/ColumnFormatterTrait.php b/src/ColumnFormatterTrait.php deleted file mode 100644 index f69c5f3..0000000 --- a/src/ColumnFormatterTrait.php +++ /dev/null @@ -1,31 +0,0 @@ -column = $table . '.' . $column; - } else { - $this->column = $column; - } - } else { - $this->column = $column; - } - } -} diff --git a/src/Filter/Between.php b/src/Filter/Between.php deleted file mode 100644 index aa2507d..0000000 --- a/src/Filter/Between.php +++ /dev/null @@ -1,68 +0,0 @@ -value)) { - $value = $this->value; - $start = $this->formatValue(array_shift($value)); - $end = $this->formatValue(array_pop($value)); - $isStartEmpty = self::isEmpty($start); - $isEndEmpty = self::isEmpty($end); - - if (!$isStartEmpty && !$isEndEmpty) { - return [ - self::getOperator(), - $this->column, - $start, - $end, - ]; - } - - if (!$isStartEmpty) { - return (new GreaterThanOrEqual($this->column, $start))->toCriteriaArray(); - } - - if (!$isEndEmpty) { - return (new LessThanOrEqual($this->column, $end))->toCriteriaArray(); - } - - return []; - } - - return parent::toCriteriaArray(); - } -} diff --git a/src/Filter/CompareFilter.php b/src/Filter/CompareFilter.php deleted file mode 100644 index 563d671..0000000 --- a/src/Filter/CompareFilter.php +++ /dev/null @@ -1,72 +0,0 @@ -setColumn($column, $table); - } - - public function withIgnoreNull(bool $ignoreNull = true): static - { - $new = clone $this; - $new->ignoreNull = $ignoreNull; - - return $new; - } - - public function withDateTimeFormat(?string $format): static - { - $new = clone $this; - $new->dateTimeFormat = $format; - - return $new; - } - - protected function formatValue(mixed $value): mixed - { - $format = $this->dateTimeFormat ?? static::$mainDateTimeFormat; - - if ($format && $value instanceof DateTimeInterface) { - return $value->format($format); - } - - return $value; - } - - protected function formatValues(array $values): array - { - return array_map($this->formatValue(...), $values); - } - - public function toCriteriaArray(): array - { - if ($this->value === null) { - return $this->ignoreNull ? [] : (new IsNull($this->column))->toCriteriaArray(); - } - - if (is_array($this->value)) { - $value = $this->formatValues($this->value); - } else { - $value = $this->formatValue($this->value); - } - - return [static::getOperator(), $this->column , $value]; - } -} diff --git a/src/Filter/Equals.php b/src/Filter/Equals.php deleted file mode 100644 index 1a5d0f7..0000000 --- a/src/Filter/Equals.php +++ /dev/null @@ -1,25 +0,0 @@ -value) || $this->value instanceof QueryInterface) { - return (new In($this->column, $this->value))->toCriteriaArray(); - } - - return parent::toCriteriaArray(); - } -} diff --git a/src/Filter/EqualsEmpty.php b/src/Filter/EqualsEmpty.php deleted file mode 100644 index 9fbf074..0000000 --- a/src/Filter/EqualsEmpty.php +++ /dev/null @@ -1,74 +0,0 @@ -setColumn($column, $table); - - if ($filters !== []) { - $this->filters = $filters; - } else { - $this->filters = [ - new IsNull($this->column), - new Equals($this->column, ''), - ]; - } - } - - public function withFilter(FilterInterface $filter): self - { - $new = clone $this; - $new->filters[] = $filter; - - return $new; - } - - public function withFilters(FilterInterface $filter, FilterInterface ...$filters): self - { - $new = clone $this; - array_unshift($filters, $filter); - - $new->filters = $filters; - - return $new; - } - - /** - * @inheritDoc - */ - public static function getOperator(): string - { - return FilterEqualsEmpty::getOperator(); - } - - /** - * @inheritDoc - */ - public function toCriteriaArray(): array - { - $filters = array_values($this->filters); - - return (new Any(...$filters))->toCriteriaArray(); - } -} diff --git a/src/Filter/GreaterThan.php b/src/Filter/GreaterThan.php deleted file mode 100644 index 4bceeda..0000000 --- a/src/Filter/GreaterThan.php +++ /dev/null @@ -1,15 +0,0 @@ -setColumn($column, $table); - } - - public static function getOperator(): string - { - return EqualsNull::getOperator(); - } - - public function toCriteriaArray(): array - { - return ['IS', $this->column, null]; - } -} diff --git a/src/Filter/LessThan.php b/src/Filter/LessThan.php deleted file mode 100644 index 0049e4b..0000000 --- a/src/Filter/LessThan.php +++ /dev/null @@ -1,15 +0,0 @@ -withStart() - ->withEnd(); - } - - public function withoutBoth(): static - { - return $this - ->withoutStart() - ->withoutEnd(); - } - - public function withStart(): static - { - $new = clone $this; - $new->start = true; - - return $new; - } - - public function withoutStart(): static - { - $new = clone $this; - $new->start = false; - - return $new; - } - - public function withEnd(): static - { - $new = clone $this; - $new->end = true; - - return $new; - } - - public function withoutEnd(): static - { - $new = clone $this; - $new->end = false; - - return $new; - } - - public function toCriteriaArray(): array - { - $value = is_array($this->value) ? $this->formatValues($this->value) : $this->formatValue($this->value); - - if (!is_string($value) || ($this->start && $this->end)) { - return parent::toCriteriaArray(); - } - - if (!$this->start && !$this->end) { - return [static::getOperator(), $this->column, $value, null]; - } - - $value = $this->start ? '%' . $value : $value . '%'; - - return [static::getOperator(), $this->column, $value, null]; - } -} diff --git a/src/Filter/Not.php b/src/Filter/Not.php deleted file mode 100644 index 7da9044..0000000 --- a/src/Filter/Not.php +++ /dev/null @@ -1,79 +0,0 @@ - 'IS NOT', - 'IN' => 'NOT IN', - 'EXISTS' => 'NOT EXISTS', - 'BETWEEN' => 'NOT BETWEEN', - 'LIKE' => 'NOT LIKE', - 'ILIKE' => 'NOT ILIKE', - '>' => '<=', - '>=' => '<', - '<' => '>=', - '<=' => '>', - '=' => '!=', - ]; - - public function __construct(private FilterInterface $filter, ?array $operators = null) - { - if ($operators !== null) { - $this->operators = $operators; - } - } - - public static function getOperator(): string - { - return FilterNot::getOperator(); - } - - public function withOperator(string $operator, ?string $inverse): self - { - $new = clone $this; - $operator = strtoupper($operator); - - if ($inverse === null) { - unset($new->operators[$operator]); - } else { - $new->operators[$operator] = $inverse; - } - - return $new; - } - - public function withoutOperator(string $operator): self - { - return $this->withOperator($operator, null); - } - - public function toCriteriaArray(): array - { - $array = $this->filter->toCriteriaArray(); - - if ($array === []) { - return []; - } - - $key = array_key_first($array); - $operator = is_string($array[$key]) ? strtoupper($array[$key]) : null; - - if ($operator !== null && isset($this->operators[$operator])) { - $array[0] = $this->operators[$operator]; - } else { - $array = [self::getOperator(), $array]; - } - - return $array; - } -} diff --git a/src/Filter/NotEquals.php b/src/Filter/NotEquals.php deleted file mode 100644 index c1169cb..0000000 --- a/src/Filter/NotEquals.php +++ /dev/null @@ -1,30 +0,0 @@ -value === null) { - if ($this->ignoreNull) { - return []; - } - - $isNull = new IsNull($this->column); - - return (new Not($isNull))->toCriteriaArray(); - } - - $value = $this->formatValue($this->value); - - return [self::getOperator(), $this->column, $value]; - } -} diff --git a/src/Filter/OrILike.php b/src/Filter/OrILike.php deleted file mode 100644 index a9395b1..0000000 --- a/src/Filter/OrILike.php +++ /dev/null @@ -1,13 +0,0 @@ - self::makeGroup($operator, $operands), + 'not' => self::makeNot($operator, $operands), + 'like' => self::makeLike($operator, $operands), + 'between' => self::makeBetween($operator, $operands), + 'in' => self::makeIn($operator, $operands), + 'empty' => self::makeEmpty($operator, $operands), + 'null' => self::makeNull($operator, $operands), + '=' => self::makeEquals($operator, $operands), + '>', '<', '>=', '<=' => self::makeCompare($operator, $operands), + 'exists' => self::makeExists($operator, $operands), + default => throw new LogicException(sprintf('Not supported operator: %s.', $operator)), + }; + } + + private static function makeGroup(string $operator, array $operands): ?array + { + if (!array_key_exists(0, $operands)) { + throw new LogicException( + sprintf( + 'Not found parameter for the "%s" operator.', + $operator, + ) + ); + } + if (!is_array($operands[0])) { + throw new LogicException( + sprintf( + 'The parameter for "%s" operator must be an array. Got %s.', + $operator, + get_debug_type($operands[0]) + ) + ); + } + if (empty($operands[0])) { + return null; + } + $condition = [strtoupper($operator)]; + foreach ($operands[0] as $subCriteria) { + if (!is_array($subCriteria)) { + throw new LogicException('Incorrect sub-criteria.'); + } + $condition[] = self::make($subCriteria); + } + return $condition; + } + + private static function makeNot(string $operator, array $operands): ?array + { + if ( + array_keys($operands) !== [0] + || !is_array($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "not" operator.'); + } + if (empty($operands[0])) { + return null; + } + + $subCondition = self::make($operands[0]); + + if (isset($subCondition[0]) && is_string($subCondition[0])) { + $convert = [ + 'IS' => 'IS NOT', + 'IN' => 'NOT IN', + 'EXISTS' => 'NOT EXISTS', + 'BETWEEN' => 'NOT BETWEEN', + 'LIKE' => 'NOT LIKE', + 'ILIKE' => 'NOT ILIKE', + '>' => '<=', + '>=' => '<', + '<' => '>=', + '<=' => '>', + '=' => '!=', + ]; + $operator = strtoupper($subCondition[0]); + if (isset($convert[$operator])) { + $subCondition[0] = $convert[$operator]; + return $subCondition; + } + } + + return ['NOT', $subCondition]; + } + + private static function makeLike(string $operator, array $criteria): array + { + if ( + array_keys($criteria) !== [0, 1] + || !is_string($criteria[0]) + || !is_string($criteria[1]) + ) { + throw new LogicException('Incorrect criteria for the "like" operator.'); + } + return ['LIKE', $criteria[0], $criteria[1]]; + } + + private static function makeBetween(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0, 1, 2] + || !is_string($operands[0]) + || !(is_scalar($operands[1]) || $operands[1] instanceof DateTimeInterface) + || !(is_scalar($operands[2]) || $operands[2] instanceof DateTimeInterface) + ) { + throw new LogicException('Incorrect criteria for the "between" operator.'); + } + $from = $operands[1] instanceof DateTimeInterface + ? $operands[1]->format('Y-m-d H:i:s') + : $operands[1]; + $to = $operands[2] instanceof DateTimeInterface + ? $operands[2]->format('Y-m-d H:i:s') + : $operands[2]; + return ['BETWEEN', $operands[0], $from, $to]; + } + + private static function makeIn(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || !is_array($operands[1]) + ) { + throw new LogicException('Incorrect criteria for the "in" operator.'); + } + return ['IN', $operands[0], $operands[1]]; + } + + private static function makeEmpty(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0] + || !is_string($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "empty" operator.'); + } + return ['OR', ['IS', $operands[0], null], ['=', $operands[0], '']]; + } + + private static function makeNull(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0] + || !is_string($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "empty" operator.'); + } + return ['IS', $operands[0], null]; + } + + private static function makeEquals(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || ( + !is_string($operands[1]) + && !(is_scalar($operands[1]) || is_null($operands[1]) || $operands[1] instanceof DateTimeInterface) + ) + ) { + throw new LogicException('Incorrect criteria for the "=" operator.'); + } + + if ($operands[1] === null) { + return ['IS NULL', $operands[0]]; + } + + $value = $operands[1] instanceof DateTimeInterface + ? $operands[1]->format('Y-m-d H:i:s') + : $operands[1]; + + return ['=', $operands[0], $value]; + } + + private static function makeCompare(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || ( + !is_string($operands[1]) + && !(is_scalar($operands[1]) || $operands[1] instanceof DateTimeInterface) + ) + ) { + throw new LogicException(sprintf('Incorrect criteria for the "%s" operator.', $operator)); + } + + $value = $operands[1] instanceof DateTimeInterface + ? $operands[1]->format('Y-m-d H:i:s') + : $operands[1]; + + return [$operator, $operands[0], $value]; + } + + private static function makeExists(string $operator, array $operands): array + { + if ( + array_keys($operands) !== [0] + || !$operands[0] instanceof QueryInterface + ) { + throw new LogicException('Incorrect criteria for the "exists" operator.'); + } + + return ['EXISTS', $operands[0]]; } } diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index 824c733..cf17c32 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -4,7 +4,7 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Db\Filter\EqualsEmpty; +use Yiisoft\Data\Reader\Filter\EqualsEmpty; final class EqualsEmptyHandler extends BaseHandler { diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index 8acbc95..0d587e9 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -4,7 +4,7 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Db\Filter\Equals; +use Yiisoft\Data\Reader\Filter\Equals; final class EqualsHandler extends BaseHandler { diff --git a/src/FilterHandler/EqualsNullHandler.php b/src/FilterHandler/EqualsNullHandler.php new file mode 100644 index 0000000..02d0896 --- /dev/null +++ b/src/FilterHandler/EqualsNullHandler.php @@ -0,0 +1,15 @@ + [ new Equals('equals', 1), '[equals] = 1', ], - - //BetweenHandler - [ + 'between' => [ new Between('column', 100, 300), '[column] BETWEEN 100 AND 300', ], - //GreaterThanHandler - [ + 'greater-than' => [ new GreaterThan('column', 1000), '[column] > 1000', ], - [ + 'greater-than-or-equal' => [ new GreaterThanOrEqual('column', 3.5), '[column] >= 3.5', ], - [ + 'less-than' => [ new LessThan('column', 10.7), '[column] < 10.7', ], - [ + 'less-than-or-equal' => [ new LessThanOrEqual('column', 100), '[column] <= 100', ], - [ + 'in' => [ new In('column', [10, 20, 30]), '[column] IN (10, 20, 30)', ], - //LikeHandler - [ + 'like' => [ new Like('column', 'foo'), "[column] LIKE '%foo%'", ], @@ -88,14 +83,40 @@ public function testSimpleFilter(FilterInterface $filter, string $condition): vo public function notDataProvider(): array { - $filters = $this->simpleDataProvider(); - - foreach ($filters as $i => $filter) { - $filters[$i][0] = new Not($filter[0]); - $filters[$i][1] = 'NOT (' . $filter[1] . ')'; - } - - return $filters; + return [ + 'equals' => [ + new Not(new Equals('equals', 1)), + '[equals] != 1', + ], + 'between' => [ + new Not(new Between('column', 100, 300)), + '[column] NOT BETWEEN 100 AND 300', + ], + 'greater-than' => [ + new Not(new GreaterThan('column', 1000)), + '[column] <= 1000', + ], + 'greater-than-or-equal' => [ + new Not(new GreaterThanOrEqual('column', 3.5)), + '[column] < 3.5', + ], + 'less-than' => [ + new Not(new LessThan('column', 10.7)), + '[column] >= 10.7', + ], + 'less-than-or-equal' => [ + new Not(new LessThanOrEqual('column', 100)), + '[column] > 100', + ], + 'in' => [ + new Not(new In('column', [10, 20, 30])), + '[column] NOT IN (10, 20, 30)', + ], + 'like' => [ + new Not(new Like('column', 'foo')), + "[column] NOT LIKE '%foo%'", + ], + ]; } /** diff --git a/tests/DataReaderTest.php b/tests/DataReaderTest.php index 8171276..9720e8e 100644 --- a/tests/DataReaderTest.php +++ b/tests/DataReaderTest.php @@ -6,31 +6,27 @@ use PHPUnit\Framework\TestCase; use stdClass; -use Yiisoft\Data\Db\Filter\Between; -use Yiisoft\Data\Db\Filter\Equals; -use Yiisoft\Data\Db\Filter\GreaterThan; -use Yiisoft\Data\Db\Filter\GreaterThanOrEqual; -use Yiisoft\Data\Db\Filter\ILike; -use Yiisoft\Data\Db\Filter\In; -use Yiisoft\Data\Db\Filter\LessThan; -use Yiisoft\Data\Db\Filter\LessThanOrEqual; -use Yiisoft\Data\Db\Filter\Like; -use Yiisoft\Data\Db\Filter\NotEquals; use Yiisoft\Data\Db\FilterHandler\BetweenHandler; use Yiisoft\Data\Db\FilterHandler\EqualsHandler; use Yiisoft\Data\Db\FilterHandler\GreaterThanHandler; use Yiisoft\Data\Db\FilterHandler\GreaterThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\ILikeHandler; use Yiisoft\Data\Db\FilterHandler\InHandler; use Yiisoft\Data\Db\FilterHandler\LessThanHandler; use Yiisoft\Data\Db\FilterHandler\LessThanOrEqualHandler; use Yiisoft\Data\Db\FilterHandler\LikeHandler; -use Yiisoft\Data\Db\FilterHandler\NotEqualsHandler; use Yiisoft\Data\Db\QueryDataReader; use Yiisoft\Data\Db\Tests\Support\CustomerDataReader; use Yiisoft\Data\Db\Tests\Support\CustomerDTO; use Yiisoft\Data\Db\Tests\Support\CustomerQuery; use Yiisoft\Data\Db\Tests\Support\TestTrait; +use Yiisoft\Data\Reader\Filter\Between; +use Yiisoft\Data\Reader\Filter\Equals; +use Yiisoft\Data\Reader\Filter\GreaterThan; +use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual; +use Yiisoft\Data\Reader\Filter\In; +use Yiisoft\Data\Reader\Filter\LessThan; +use Yiisoft\Data\Reader\Filter\LessThanOrEqual; +use Yiisoft\Data\Reader\Filter\Like; use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Data\Reader\Sort; use Yiisoft\Db\Expression\Expression; @@ -171,7 +167,7 @@ public static function handlerDataProvider(): array EqualsHandler::class, ], [ - new Between('column', [100, 300]), + new Between('column', 100, 300), BetweenHandler::class, ], [ @@ -194,18 +190,10 @@ public static function handlerDataProvider(): array new In('column', [10, 20, 30]), InHandler::class, ], - [ - new NotEquals('column', 40), - NotEqualsHandler::class, - ], [ new Like('column', 'foo'), LikeHandler::class, ], - [ - new ILike('column', 'foo'), - ILikeHandler::class, - ], ]; } diff --git a/tests/FiltersTest.php b/tests/FiltersTest.php deleted file mode 100644 index e38f2f8..0000000 --- a/tests/FiltersTest.php +++ /dev/null @@ -1,340 +0,0 @@ -=', 'column', 100], - ], - [ - new Between('column', [null, 250]), - ['<=', 'column', 250], - ], - [ - new Between('column', [new DateTime('2011-01-01T15:00:01'), new DateTime('2011-01-01T15:10:01')]), - ['between', 'column', '2011-01-01 15:00:01 +00:00', '2011-01-01 15:10:01 +00:00'], - ], - //GreaterThanHandler - [ - new GreaterThan('column', 1000), - ['>', 'column', 1000], - ], - [ - new GreaterThan('column', new DateTime('2011-01-01T15:00:01')), - ['>', 'column', '2011-01-01 15:00:01 +00:00'], - ], - [ - new GreaterThanOrEqual('column', 3.5), - ['>=', 'column', 3.5], - ], - [ - new LessThan('column', 10.7), - ['<', 'column', 10.7], - ], - [ - new LessThanOrEqual('column', 100), - ['<=', 'column', 100], - ], - [ - new In('column', [10, 20, 30]), - ['in', 'column', [10, 20, 30]], - ], - //NotHandler equals - [ - new NotEquals('column', 40), - ['!=', 'column', 40], - ], - //LikeHandler - [ - new Like('column', 'foo'), - ['like', 'column', 'foo'], - ], - [ - (new Like('column', 'foo'))->withoutStart(), - ['like', 'column', 'foo%', null], - ], - [ - (new Like('column', 'foo'))->withoutEnd(), - ['like', 'column', '%foo', null], - ], - [ - (new Like('column', 'foo'))->withoutBoth(), - ['like', 'column', 'foo', null], - ], - //ILikeHandler - [ - new ILike('column', 'foo'), - ['ilike', 'column', 'foo'], - ], - [ - (new ILike('column', 'foo'))->withoutStart(), - ['ilike', 'column', 'foo%', null], - ], - [ - (new ILike('column', 'foo'))->withoutEnd(), - ['ilike', 'column', '%foo', null], - ], - [ - (new ILike('column', 'foo'))->withoutBoth(), - ['ilike', 'column', 'foo', null], - ], - ]; - } - - public function nullDataProvider(): array - { - return [ - [ - new Equals('column', null), - ], - [ - new Between('column', null), - ], - [ - new GreaterThan('column', null), - ], - [ - new GreaterThanOrEqual('column', null), - ], - [ - new LessThan('column', null), - ], - [ - new LessThanOrEqual('column', null), - ], - [ - new In('column', null), - ], - [ - new NotEquals('column', null), - ['IS NOT', 'column', null], - ], - [ - new Like('column', null), - ], - [ - (new Like('column', null))->withoutStart(), - ], - [ - (new Like('column', null))->withoutEnd(), - ], - [ - (new Like('column', null))->withoutBoth(), - ], - [ - new ILike('column', null), - ], - [ - (new ILike('column', null))->withoutStart(), - ], - [ - (new ILike('column', null))->withoutEnd(), - ], - [ - (new ILike('column', null))->withoutBoth(), - ], - ]; - } - - public function groupDataProvider(): array - { - $filters = array_column($this->simpleDataProvider(), 0); - $nullFilters = array_column($this->nullDataProvider(), 0); - $map = array_map(static fn (CompareFilter $filter) => $filter - ->withDateTimeFormat('Y-m-d H:i:s P') - ->toCriteriaArray(), $filters); - $nullMap = array_map(static fn ($filter) => $filter->withIgnoreNull(), $nullFilters); - - return [ - //AND - [ - new All(...$filters), - array_merge(['and'], $map), - ], - [ - new All(...$nullMap), - [], - ], - //OR - [ - new Any(...$filters), - array_merge(['or'], $map), - ], - [ - new Any(...$nullMap), - [], - ], - ]; - } - - /** - * @dataProvider simpleDataProvider - */ - public function testSimpleFilter(FilterInterface $filter, array $expected): void - { - $this->assertSame($expected, $filter->toCriteriaArray()); - } - - /** - * @dataProvider nullDataProvider - */ - public function testWithNull(FilterInterface $filter, array $expected = ['IS', 'column', null]): void - { - $this->assertSame($expected, $filter->toCriteriaArray()); - $this->assertSame([], $filter->withIgnoreNull()->toCriteriaArray()); - } - - /** - * @dataProvider simpleDataProvider - */ - public function testNotFilter(FilterInterface $filter, array $expected): void - { - $not = new Not($filter); - $array = $filter->toCriteriaArray(); - - switch (strtolower($array[0])) { - case 'in': - case 'between': - case 'like': - case 'ilike': - $array[0] = 'NOT ' . mb_strtoupper($array[0]); - $expected = $array; - break; - - case '>': - $array[0] = '<='; - $expected = $array; - break; - - case '>=': - $array[0] = '<'; - $expected = $array; - break; - - case '<': - $array[0] = '>='; - $expected = $array; - break; - - case '<=': - $array[0] = '>'; - $expected = $array; - break; - - case 'is': - $array[0] = 'IS NOT'; - $expected = $array; - break; - - case '=': - $array[0] = '!='; - $expected = $array; - - break; - - default: - $expected = ['not', $expected]; - } - - $this->assertSame($expected, $not->toCriteriaArray()); - } - - /** - * @dataProvider groupDataProvider - */ - public function testGroupFilters(All|Any $filter, array $expected): void - { - $this->assertSame($expected, $filter->toCriteriaArray()); - } - - public function equalsEmptyDataProvider(): array - { - return [ - [ - new EqualsEmpty('column'), - [ - 'or', - ['IS', 'column', null], - ['=', 'column', ''], - ], - ], - [ - (new EqualsEmpty('column'))->withFilter(new Equals('column', 0)), - [ - 'or', - ['IS', 'column', null], - ['=', 'column', ''], - ['=', 'column', 0], - ], - ], - [ - (new EqualsEmpty('column'))->withFilters(new LessThanOrEqual('column', 10)), - [ - 'or', - ['<=', 'column', 10], - ], - ], - ]; - } - - /** - * @dataProvider equalsEmptyDataProvider - */ - public function testEqualsEmpty(EqualsEmpty $filter, array $expected): void - { - $this->assertSame($filter->toCriteriaArray(), $expected); - } -} diff --git a/tests/QueryWithFiltersTest.php b/tests/QueryWithFiltersTest.php index 6d47bb6..181b063 100644 --- a/tests/QueryWithFiltersTest.php +++ b/tests/QueryWithFiltersTest.php @@ -6,75 +6,51 @@ use DateTime; use PHPUnit\Framework\TestCase; -use Yiisoft\Data\Db\Filter\Between; -use Yiisoft\Data\Db\Filter\CompareFilter; -use Yiisoft\Data\Db\Filter\Equals; -use Yiisoft\Data\Db\Filter\GreaterThan; -use Yiisoft\Data\Db\Filter\GreaterThanOrEqual; -use Yiisoft\Data\Db\Filter\GroupFilter; -use Yiisoft\Data\Db\Filter\In; -use Yiisoft\Data\Db\Filter\IsNull; -use Yiisoft\Data\Db\Filter\LessThan; -use Yiisoft\Data\Db\Filter\LessThanOrEqual; -use Yiisoft\Data\Db\Filter\Like; -use Yiisoft\Data\Db\Filter\Not; -use Yiisoft\Data\Db\Filter\NotEquals; -use Yiisoft\Data\Db\Filter\OrLike; use Yiisoft\Data\Db\QueryDataReader; use Yiisoft\Data\Db\Tests\Support\TestTrait; use Yiisoft\Data\Reader\Filter\All; use Yiisoft\Data\Reader\Filter\Any; -use Yiisoft\Db\Expression\Expression; +use Yiisoft\Data\Reader\Filter\Between; +use Yiisoft\Data\Reader\Filter\Equals; +use Yiisoft\Data\Reader\Filter\EqualsNull; +use Yiisoft\Data\Reader\Filter\GreaterThan; +use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual; +use Yiisoft\Data\Reader\Filter\In; +use Yiisoft\Data\Reader\Filter\LessThan; +use Yiisoft\Data\Reader\Filter\LessThanOrEqual; +use Yiisoft\Data\Reader\Filter\Like; +use Yiisoft\Data\Reader\Filter\Not; +use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Db\Query\Query; final class QueryWithFiltersTest extends TestCase { use TestTrait; - public static function setUpBeforeClass(): void - { - CompareFilter::$mainDateTimeFormat = 'Y-m-d H:i:s'; - } - public function simpleDataProvider(): array { return [ - //EqualsHandler - [ + 'equals' => [ new Equals('equals', 1), '[equals] = 1', ], - [ - new Equals('equals', [1, 2, 3]), - '[equals] IN (1, 2, 3)', - ], - [ + 'equals-datetime' => [ new Equals('column', new DateTime('2011-01-01T15:03:01.012345Z')), "[column] = '2011-01-01 15:03:01'", ], - //BetweenHandler - [ - new Between('column', [100, 300]), + 'between' => [ + new Between('column', 100, 300), '[column] BETWEEN 100 AND 300', ], - [ - new Between('column', [100, null]), - '[column] >= 100', - ], - [ - new Between('column', [null, 250]), - '[column] <= 250', - ], - [ - new Between('column', [new DateTime('2011-01-01T15:00:01'), new DateTime('2011-01-01T15:10:01')]), + 'between-dates' => [ + new Between('column', new DateTime('2011-01-01T15:00:01'), new DateTime('2011-01-01T15:10:01')), "[column] BETWEEN '2011-01-01 15:00:01' AND '2011-01-01 15:10:01'", ], - //GreaterThanHandler - [ + 'greater-than' => [ new GreaterThan('column', 1000), '[column] > 1000', ], - [ + 'greater-than-date' => [ new GreaterThan('column', new DateTime('2011-01-01T15:00:01')), "[column] > '2011-01-01 15:00:01'", ], @@ -94,44 +70,17 @@ public function simpleDataProvider(): array new In('column', [10, 20.5, 30]), '[column] IN (10, 20.5, 30)', ], - //NotHandler equals - [ - new NotEquals('column', 40), - '[column] != 40', - ], - //LikeHandler - [ + 'like' => [ new Like('column', 'foo'), "[column] LIKE '%foo%'", ], - [ - (new Like('column', 'foo'))->withoutStart(), - "[column] LIKE 'foo%'", - ], - [ - (new Like('column', 'foo'))->withoutEnd(), - "[column] LIKE '%foo'", - ], - [ - (new Like('column', 'foo'))->withoutBoth(), - "[column] LIKE 'foo'", - ], - [ - new Like('column', new Expression("CONCAT([[foo]] ->> 'bar', '%')")), - "[column] LIKE CONCAT([foo] ->> 'bar', '%')", - ], - //Array Or FilterLike - [ - new OrLike('column', ['foo', 'bar']), - "[column] LIKE '%foo%' OR [column] LIKE '%bar%'", - ], ]; } /** * @dataProvider simpleDataProvider */ - public function testSimpleFilter(CompareFilter $filter, string $condition): void + public function testSimpleFilter(FilterInterface $filter, string $condition): void { $db = $this->getConnection(); $query = (new Query($db)) @@ -151,7 +100,7 @@ public function testSimpleFilter(CompareFilter $filter, string $condition): void /** * @dataProvider simpleDataProvider */ - public function testSimpleHaving(CompareFilter $having, string $condition): void + public function testSimpleHaving(FilterInterface $having, string $condition): void { $db = $this->getConnection(); $query = (new Query($db)) @@ -173,9 +122,9 @@ public function groupFilterDataProvider(): array return [ [ new All( - new IsNull('null_column'), + new EqualsNull('null_column'), new Equals('equals', 10), - new Between('between', [10, 20]), + new Between('between', 10, 20), new Any( new Equals('id', 8), new Like('name', 'foo') @@ -200,19 +149,23 @@ public function groupFilterDataProvider(): array ['>', 'id', 88], [ 'or', - ['=', 'state', 2], - ['like', 'name', 'eva'], + [ + ['=', 'state', 2], + ['like', 'name', 'eva'], + ], ], ]), "([id] > 88) AND (([state] = 2) OR ([name] LIKE '%eva%'))", ], [ - (new All())->withCriteriaArray([ + (new Any())->withCriteriaArray([ ['>', 'id', 88], [ 'and', - ['=', 'state', 2], - ['like', 'name', 'eva'], + [ + ['=', 'state', 2], + ['like', 'name', 'eva'], + ], ], ]), "([id] > 88) OR (([state] = 2) AND ([name] LIKE '%eva%'))", @@ -222,8 +175,10 @@ public function groupFilterDataProvider(): array ['>', 'id', 88], [ 'or', - ['=', 'state', 2], - ['like', 'name', 'eva'], + [ + ['=', 'state', 2], + ['like', 'name', 'eva'], + ], ], ]), "([id] > 88) OR (([state] = 2) OR ([name] LIKE '%eva%'))", @@ -233,8 +188,10 @@ public function groupFilterDataProvider(): array ['>', 'id', 88], [ 'and', - ['=', 'state', 2], - ['like', 'name', 'eva'], + [ + ['=', 'state', 2], + ['like', 'name', 'eva'], + ], ], ]), "([id] > 88) AND (([state] = 2) AND ([name] LIKE '%eva%'))", From 3435b934f90a5c516234eb72b43b841bbce19841 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:09:34 +0300 Subject: [PATCH 05/16] refactor --- src/AbstractQueryDataReader.php | 12 +++++++---- src/FilterHandler/BaseHandler.php | 22 ++------------------- src/FilterHandler/QueryHandlerInterface.php | 5 +---- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index 653d9ae..c7aadd8 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -173,8 +173,10 @@ public function getHandlerByOperation(string|FilterInterface $operation): QueryH protected function applyFilter(QueryInterface $query): QueryInterface { if ($this->filter !== null) { - $query = $this->getHandlerByOperation($this->filter) - ->applyFilter($query, $this->filter); + $condition = $this->getHandlerByOperation($this->filter)->getCondition($this->filter); + if ($condition !== null) { + $query = $query->andWhere($condition); + } } return $query; @@ -183,8 +185,10 @@ protected function applyFilter(QueryInterface $query): QueryInterface protected function applyHaving(QueryInterface $query): QueryInterface { if ($this->having !== null) { - $query = $this->getHandlerByOperation($this->having) - ->applyHaving($query, $this->having); + $condition = $this->getHandlerByOperation($this->having)->getCondition($this->having); + if ($condition !== null) { + $query = $query->andHaving($condition); + } } return $query; diff --git a/src/FilterHandler/BaseHandler.php b/src/FilterHandler/BaseHandler.php index 5bcedf0..3a275ce 100644 --- a/src/FilterHandler/BaseHandler.php +++ b/src/FilterHandler/BaseHandler.php @@ -5,29 +5,11 @@ namespace Yiisoft\Data\Db\FilterHandler; use Yiisoft\Data\Reader\FilterInterface; -use Yiisoft\Db\Query\QueryInterface; abstract class BaseHandler implements QueryHandlerInterface { - public function applyFilter(QueryInterface $query, FilterInterface $filter): QueryInterface + public function getCondition(FilterInterface $filter): ?array { - $condition = ConditionFactory::make($filter->toCriteriaArray()); - - if ($condition === null) { - return $query; - } - - return $query->andWhere($condition); - } - - public function applyHaving(QueryInterface $query, FilterInterface $having): QueryInterface - { - $condition = ConditionFactory::make($having->toCriteriaArray()); - - if ($condition === null) { - return $query; - } - - return $query->andHaving($condition); + return ConditionFactory::make($filter->toCriteriaArray()); } } diff --git a/src/FilterHandler/QueryHandlerInterface.php b/src/FilterHandler/QueryHandlerInterface.php index 695cec2..eca466c 100644 --- a/src/FilterHandler/QueryHandlerInterface.php +++ b/src/FilterHandler/QueryHandlerInterface.php @@ -6,11 +6,8 @@ use Yiisoft\Data\Reader\FilterHandlerInterface; use Yiisoft\Data\Reader\FilterInterface; -use Yiisoft\Db\Query\QueryInterface; interface QueryHandlerInterface extends FilterHandlerInterface { - public function applyFilter(QueryInterface $query, FilterInterface $filter): QueryInterface; - - public function applyHaving(QueryInterface $query, FilterInterface $having): QueryInterface; + public function getCondition(FilterInterface $filter): ?array; } From 2fb817cdd3a4fdc715e816f54871399a1b2a3881 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:20:26 +0300 Subject: [PATCH 06/16] refactor --- src/AbstractQueryDataReader.php | 22 +++++++++++++-- src/FilterHandler/BaseHandler.php | 4 +-- src/FilterHandler/ConditionFactory.php | 30 +++++++++++---------- src/FilterHandler/QueryHandlerInterface.php | 3 +-- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index c7aadd8..55e2622 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -6,6 +6,7 @@ use Generator; use InvalidArgumentException; +use LogicException; use RuntimeException; use Yiisoft\Data\Db\FilterHandler\AllHandler; use Yiisoft\Data\Db\FilterHandler\AnyHandler; @@ -173,7 +174,7 @@ public function getHandlerByOperation(string|FilterInterface $operation): QueryH protected function applyFilter(QueryInterface $query): QueryInterface { if ($this->filter !== null) { - $condition = $this->getHandlerByOperation($this->filter)->getCondition($this->filter); + $condition = $this->getCondition($this->filter); if ($condition !== null) { $query = $query->andWhere($condition); } @@ -185,7 +186,7 @@ protected function applyFilter(QueryInterface $query): QueryInterface protected function applyHaving(QueryInterface $query): QueryInterface { if ($this->having !== null) { - $condition = $this->getHandlerByOperation($this->having)->getCondition($this->having); + $condition = $this->getCondition($this->having); if ($condition !== null) { $query = $query->andHaving($condition); } @@ -380,4 +381,21 @@ public function readOne(): array|object|null * @psalm-return TValue */ abstract protected function createItem(array|object $row): array|object; + + private function getCondition(FilterInterface $filter): ?array + { + $criteria = $filter->toCriteriaArray(); + if (!isset($criteria[0])) { + throw new LogicException('Incorrect criteria array.'); + } + + $operator = $criteria[0]; + if (!is_string($operator)) { + throw new LogicException('Criteria operator must be a string.'); + } + + $operands = array_slice($criteria, 1); + + return $this->getHandlerByOperation($filter)->getCondition($operator, $operands); + } } diff --git a/src/FilterHandler/BaseHandler.php b/src/FilterHandler/BaseHandler.php index 3a275ce..e11373c 100644 --- a/src/FilterHandler/BaseHandler.php +++ b/src/FilterHandler/BaseHandler.php @@ -8,8 +8,8 @@ abstract class BaseHandler implements QueryHandlerInterface { - public function getCondition(FilterInterface $filter): ?array + public function getCondition(string $operator, array $operands): ?array { - return ConditionFactory::make($filter->toCriteriaArray()); + return ConditionFactory::make($operator, $operands); } } diff --git a/src/FilterHandler/ConditionFactory.php b/src/FilterHandler/ConditionFactory.php index cea527e..d1b5a25 100644 --- a/src/FilterHandler/ConditionFactory.php +++ b/src/FilterHandler/ConditionFactory.php @@ -13,19 +13,8 @@ */ final class ConditionFactory { - public static function make(array $criteria): ?array + public static function make(string $operator, array $operands): ?array { - if (!isset($criteria[0])) { - throw new LogicException('Incorrect criteria array.'); - } - - $operator = $criteria[0]; - if (!is_string($operator)) { - throw new LogicException('Operator must be a string.'); - } - - $operands = array_slice($criteria, 1); - return match ($operator) { 'and', 'or' => self::makeGroup($operator, $operands), 'not' => self::makeNot($operator, $operands), @@ -68,7 +57,13 @@ private static function makeGroup(string $operator, array $operands): ?array if (!is_array($subCriteria)) { throw new LogicException('Incorrect sub-criteria.'); } - $condition[] = self::make($subCriteria); + if (!isset($subCriteria[0])) { + throw new LogicException('Incorrect sub-criteria array.'); + } + if (!is_string($subCriteria[0])) { + throw new LogicException('Sub-criteria operator must be a string.'); + } + $condition[] = self::make($subCriteria[0], array_slice($subCriteria, 1)); } return $condition; } @@ -85,7 +80,14 @@ private static function makeNot(string $operator, array $operands): ?array return null; } - $subCondition = self::make($operands[0]); + if (!isset($operands[0][0])) { + throw new LogicException('Incorrect sub-criteria array.'); + } + if (!is_string($operands[0][0])) { + throw new LogicException('Sub-criteria operator must be a string.'); + } + + $subCondition = self::make($operands[0][0], array_slice($operands[0], 1)); if (isset($subCondition[0]) && is_string($subCondition[0])) { $convert = [ diff --git a/src/FilterHandler/QueryHandlerInterface.php b/src/FilterHandler/QueryHandlerInterface.php index eca466c..016927c 100644 --- a/src/FilterHandler/QueryHandlerInterface.php +++ b/src/FilterHandler/QueryHandlerInterface.php @@ -5,9 +5,8 @@ namespace Yiisoft\Data\Db\FilterHandler; use Yiisoft\Data\Reader\FilterHandlerInterface; -use Yiisoft\Data\Reader\FilterInterface; interface QueryHandlerInterface extends FilterHandlerInterface { - public function getCondition(FilterInterface $filter): ?array; + public function getCondition(string $operator, array $operands): ?array; } From 5166b39b4616a648984cc698887690e98620cb82 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:45:26 +0300 Subject: [PATCH 07/16] refactor --- src/AbstractQueryDataReader.php | 85 +++-------------------- src/FilterHandler.php | 119 ++++++++++++++++++++++++++++++++ tests/DataReaderTest.php | 56 --------------- 3 files changed, 129 insertions(+), 131 deletions(-) create mode 100644 src/FilterHandler.php diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index 55e2622..1098f2e 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -41,6 +41,8 @@ */ abstract class AbstractQueryDataReader implements QueryDataReaderInterface { + private FilterHandler $filterHandler; + private ?Sort $sort = null; private ?FilterInterface $filter = null; private ?FilterInterface $having = null; @@ -60,30 +62,12 @@ abstract class AbstractQueryDataReader implements QueryDataReaderInterface private ?int $batchSize = 100; private ?string $countParam = null; - /** - * @var QueryHandlerInterface[] - * @psalm-var array - */ - protected array $filterHandlers = []; - - public function __construct(private QueryInterface $query) + public function __construct( + private QueryInterface $query, + ?FilterHandler $filterHandler = null, + ) { - $this->filterHandlers = $this->prepareHandlers( - new AllHandler(), - new AnyHandler(), - new EqualsHandler(), - new GreaterThanHandler(), - new GreaterThanOrEqualHandler(), - new LessThanHandler(), - new LessThanOrEqualHandler(), - new LikeHandler(), - new InHandler(), - new ExistsHandler(), - new NotHandler(), - new BetweenHandler(), - new EqualsNullHandler(), - new EqualsEmptyHandler() - ); + $this->filterHandler = $filterHandler ?? new FilterHandler(); } /** @@ -158,23 +142,10 @@ public function getPreparedQuery(): QueryInterface return $query; } - public function getHandlerByOperation(string|FilterInterface $operation): QueryHandlerInterface - { - if (is_object($operation)) { - $operation = $operation::getOperator(); - } - - if (!isset($this->filterHandlers[$operation])) { - throw new RuntimeException(sprintf('Operation "%s" is not supported', $operation)); - } - - return $this->filterHandlers[$operation]; - } - protected function applyFilter(QueryInterface $query): QueryInterface { if ($this->filter !== null) { - $condition = $this->getCondition($this->filter); + $condition = $this->filterHandler->handle($this->filter); if ($condition !== null) { $query = $query->andWhere($condition); } @@ -186,7 +157,7 @@ protected function applyFilter(QueryInterface $query): QueryInterface protected function applyHaving(QueryInterface $query): QueryInterface { if ($this->having !== null) { - $condition = $this->getCondition($this->having); + $condition = $this->filterHandler->handle($this->having); if ($condition !== null) { $query = $query->andHaving($condition); } @@ -308,29 +279,10 @@ public function withFilterHandlers(FilterHandlerInterface ...$filterHandlers): s { $new = clone $this; $new->count = $new->data = null; - $new->filterHandlers = array_merge( - $this->filterHandlers, - $this->prepareHandlers(...$filterHandlers) - ); - + $new->filterHandler = $this->filterHandler->withFilterHandlers(...$filterHandlers); return $new; } - /** - * @return QueryHandlerInterface[] - * @psalm-return array - */ - private function prepareHandlers(QueryHandlerInterface ...$queryHandlers): array - { - $handlers = []; - - foreach ($queryHandlers as $handler) { - $handlers[$handler->getOperator()] = $handler; - } - - return $handlers; - } - public function getSort(): ?Sort { return $this->sort; @@ -381,21 +333,4 @@ public function readOne(): array|object|null * @psalm-return TValue */ abstract protected function createItem(array|object $row): array|object; - - private function getCondition(FilterInterface $filter): ?array - { - $criteria = $filter->toCriteriaArray(); - if (!isset($criteria[0])) { - throw new LogicException('Incorrect criteria array.'); - } - - $operator = $criteria[0]; - if (!is_string($operator)) { - throw new LogicException('Criteria operator must be a string.'); - } - - $operands = array_slice($criteria, 1); - - return $this->getHandlerByOperation($filter)->getCondition($operator, $operands); - } } diff --git a/src/FilterHandler.php b/src/FilterHandler.php new file mode 100644 index 0000000..4d8e510 --- /dev/null +++ b/src/FilterHandler.php @@ -0,0 +1,119 @@ + + */ + private array $handlers; + + public function __construct(QueryHandlerInterface ...$handlers) + { + if (empty($handlers)) { + $handlers = [ + new AllHandler(), + new AnyHandler(), + new EqualsHandler(), + new GreaterThanHandler(), + new GreaterThanOrEqualHandler(), + new LessThanHandler(), + new LessThanOrEqualHandler(), + new LikeHandler(), + new InHandler(), + new ExistsHandler(), + new NotHandler(), + new BetweenHandler(), + new EqualsNullHandler(), + new EqualsEmptyHandler() + ]; + } + + $this->handlers = $this->prepareHandlers($handlers); + } + + public function withFilterHandlers(FilterHandlerInterface ...$handlers): self + { + foreach ($handlers as $handler) { + if (!$handler instanceof QueryHandlerInterface) { + throw new LogicException( + sprintf( + 'Filter handler must implement "%s".', + QueryHandlerInterface::class, + ) + ); + } + } + /** @var QueryHandlerInterface[] $handlers */ + + $new = clone $this; + $new->handlers = array_merge( + $this->handlers, + $this->prepareHandlers($handlers), + ); + return $new; + } + + public function handle(FilterInterface $filter): ?array + { + $criteria = $filter->toCriteriaArray(); + if (!isset($criteria[0])) { + throw new LogicException('Incorrect criteria array.'); + } + + $operator = $criteria[0]; + if (!is_string($operator)) { + throw new LogicException('Criteria operator must be a string.'); + } + + $operands = array_slice($criteria, 1); + + return $this->getHandlerByOperator($operator)->getCondition($operator, $operands); + } + + private function getHandlerByOperator(string $operator): QueryHandlerInterface + { + if (!isset($this->handlers[$operator])) { + throw new LogicException(sprintf('Operator "%s" is not supported', $operator)); + } + + return $this->handlers[$operator]; + } + + /** + * @param QueryHandlerInterface[] $handlers + * + * @return QueryHandlerInterface[] + * @psalm-return array + */ + private function prepareHandlers(array $handlers): array + { + $result = []; + foreach ($handlers as $handler) { + $result[$handler->getOperator()] = $handler; + } + return $result; + } +} diff --git a/tests/DataReaderTest.php b/tests/DataReaderTest.php index 9720e8e..ac01c52 100644 --- a/tests/DataReaderTest.php +++ b/tests/DataReaderTest.php @@ -159,44 +159,6 @@ public function testSort(Sort $sort, string $expected): void ); } - public static function handlerDataProvider(): array - { - return [ - [ - new Equals('equals', 1), - EqualsHandler::class, - ], - [ - new Between('column', 100, 300), - BetweenHandler::class, - ], - [ - new GreaterThan('column', 1000), - GreaterThanHandler::class, - ], - [ - new GreaterThanOrEqual('column', 3.5), - GreaterThanOrEqualHandler::class, - ], - [ - new LessThan('column', 10.7), - LessThanHandler::class, - ], - [ - new LessThanOrEqual('column', 100), - LessThanOrEqualHandler::class, - ], - [ - new In('column', [10, 20, 30]), - InHandler::class, - ], - [ - new Like('column', 'foo'), - LikeHandler::class, - ], - ]; - } - public function testCount(): void { $query = new CustomerQuery($this->getConnection()); @@ -205,24 +167,6 @@ public function testCount(): void self::assertEquals($query->count(), $dataReader->count()); } - /** - * @dataProvider handlerDataProvider - */ - public function testHandlerByOperation(FilterInterface $filter, string $handler): void - { - $db = $this->getConnection(); - - $query = (new Query($db)) - ->from('customer'); - - $dataReader = new QueryDataReader($query); - $filterHandler = $dataReader->getHandlerByOperation($filter); - $filterOperatorHandler = $dataReader->getHandlerByOperation($filter::getOperator()); - - self::assertInstanceOf($handler, $filterHandler); - self::assertInstanceOf($handler, $filterOperatorHandler); - } - public function testDtoCreateItem(): void { $query = new CustomerQuery($this->getConnection()); From 1f5bd2ac12dd1e27a5f40958886fa346536a50d2 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:45:57 +0300 Subject: [PATCH 08/16] remove ParameterizedTrait --- src/ParameterizedTrait.php | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/ParameterizedTrait.php diff --git a/src/ParameterizedTrait.php b/src/ParameterizedTrait.php deleted file mode 100644 index 38205a3..0000000 --- a/src/ParameterizedTrait.php +++ /dev/null @@ -1,29 +0,0 @@ -paramName = $name; - - return $new; - } - - public function getParamName(): string - { - if (!empty($this->paramName)) { - return $this->paramName; - } - - return (new ReflectionClass($this))->getShortName(); - } -} From dd2edc64452d722ad2ce411dcce2a2900c1a4875 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 17:57:30 +0300 Subject: [PATCH 09/16] refactor --- src/AbstractQueryDataReader.php | 12 +++---- ...{FilterHandler.php => CriteriaHandler.php} | 8 ++--- src/FilterHandler/BaseHandler.php | 6 ++-- src/FilterHandler/ConditionFactory.php | 32 +++++-------------- src/FilterHandler/QueryHandlerInterface.php | 3 +- 5 files changed, 22 insertions(+), 39 deletions(-) rename src/{FilterHandler.php => CriteriaHandler.php} (94%) diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index 1098f2e..6913c20 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -41,7 +41,7 @@ */ abstract class AbstractQueryDataReader implements QueryDataReaderInterface { - private FilterHandler $filterHandler; + private CriteriaHandler $criteriaHandler; private ?Sort $sort = null; private ?FilterInterface $filter = null; @@ -64,10 +64,10 @@ abstract class AbstractQueryDataReader implements QueryDataReaderInterface public function __construct( private QueryInterface $query, - ?FilterHandler $filterHandler = null, + ?CriteriaHandler $filterHandler = null, ) { - $this->filterHandler = $filterHandler ?? new FilterHandler(); + $this->criteriaHandler = $filterHandler ?? new CriteriaHandler(); } /** @@ -145,7 +145,7 @@ public function getPreparedQuery(): QueryInterface protected function applyFilter(QueryInterface $query): QueryInterface { if ($this->filter !== null) { - $condition = $this->filterHandler->handle($this->filter); + $condition = $this->criteriaHandler->handle($this->filter->toCriteriaArray()); if ($condition !== null) { $query = $query->andWhere($condition); } @@ -157,7 +157,7 @@ protected function applyFilter(QueryInterface $query): QueryInterface protected function applyHaving(QueryInterface $query): QueryInterface { if ($this->having !== null) { - $condition = $this->filterHandler->handle($this->having); + $condition = $this->criteriaHandler->handle($this->having->toCriteriaArray()); if ($condition !== null) { $query = $query->andHaving($condition); } @@ -279,7 +279,7 @@ public function withFilterHandlers(FilterHandlerInterface ...$filterHandlers): s { $new = clone $this; $new->count = $new->data = null; - $new->filterHandler = $this->filterHandler->withFilterHandlers(...$filterHandlers); + $new->criteriaHandler = $this->criteriaHandler->withFilterHandlers(...$filterHandlers); return $new; } diff --git a/src/FilterHandler.php b/src/CriteriaHandler.php similarity index 94% rename from src/FilterHandler.php rename to src/CriteriaHandler.php index 4d8e510..4f16caa 100644 --- a/src/FilterHandler.php +++ b/src/CriteriaHandler.php @@ -21,9 +21,8 @@ use Yiisoft\Data\Db\FilterHandler\NotHandler; use Yiisoft\Data\Db\FilterHandler\QueryHandlerInterface; use Yiisoft\Data\Reader\FilterHandlerInterface; -use Yiisoft\Data\Reader\FilterInterface; -final class FilterHandler +final class CriteriaHandler { /** * @psalm-var array @@ -76,9 +75,8 @@ public function withFilterHandlers(FilterHandlerInterface ...$handlers): self return $new; } - public function handle(FilterInterface $filter): ?array + public function handle(array $criteria): ?array { - $criteria = $filter->toCriteriaArray(); if (!isset($criteria[0])) { throw new LogicException('Incorrect criteria array.'); } @@ -90,7 +88,7 @@ public function handle(FilterInterface $filter): ?array $operands = array_slice($criteria, 1); - return $this->getHandlerByOperator($operator)->getCondition($operator, $operands); + return $this->getHandlerByOperator($operator)->getCondition($operator, $operands, $this); } private function getHandlerByOperator(string $operator): QueryHandlerInterface diff --git a/src/FilterHandler/BaseHandler.php b/src/FilterHandler/BaseHandler.php index e11373c..2985c47 100644 --- a/src/FilterHandler/BaseHandler.php +++ b/src/FilterHandler/BaseHandler.php @@ -4,12 +4,12 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Reader\FilterInterface; +use Yiisoft\Data\Db\CriteriaHandler; abstract class BaseHandler implements QueryHandlerInterface { - public function getCondition(string $operator, array $operands): ?array + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array { - return ConditionFactory::make($operator, $operands); + return ConditionFactory::make($operator, $operands, $criteriaHandler); } } diff --git a/src/FilterHandler/ConditionFactory.php b/src/FilterHandler/ConditionFactory.php index d1b5a25..2ccdb47 100644 --- a/src/FilterHandler/ConditionFactory.php +++ b/src/FilterHandler/ConditionFactory.php @@ -6,6 +6,7 @@ use DateTimeInterface; use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Db\Query\QueryInterface; /** @@ -13,11 +14,11 @@ */ final class ConditionFactory { - public static function make(string $operator, array $operands): ?array + public static function make(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array { return match ($operator) { - 'and', 'or' => self::makeGroup($operator, $operands), - 'not' => self::makeNot($operator, $operands), + 'and', 'or' => self::makeGroup($operator, $operands, $criteriaHandler), + 'not' => self::makeNot($operator, $operands, $criteriaHandler), 'like' => self::makeLike($operator, $operands), 'between' => self::makeBetween($operator, $operands), 'in' => self::makeIn($operator, $operands), @@ -30,7 +31,7 @@ public static function make(string $operator, array $operands): ?array }; } - private static function makeGroup(string $operator, array $operands): ?array + private static function makeGroup(string $operator, array $operands, CriteriaHandler $filterHandler): ?array { if (!array_key_exists(0, $operands)) { throw new LogicException( @@ -57,18 +58,12 @@ private static function makeGroup(string $operator, array $operands): ?array if (!is_array($subCriteria)) { throw new LogicException('Incorrect sub-criteria.'); } - if (!isset($subCriteria[0])) { - throw new LogicException('Incorrect sub-criteria array.'); - } - if (!is_string($subCriteria[0])) { - throw new LogicException('Sub-criteria operator must be a string.'); - } - $condition[] = self::make($subCriteria[0], array_slice($subCriteria, 1)); + $condition[] = $filterHandler->handle($subCriteria); } return $condition; } - private static function makeNot(string $operator, array $operands): ?array + private static function makeNot(string $operator, array $operands, CriteriaHandler $filterHandler): array { if ( array_keys($operands) !== [0] @@ -76,18 +71,7 @@ private static function makeNot(string $operator, array $operands): ?array ) { throw new LogicException('Incorrect criteria for the "not" operator.'); } - if (empty($operands[0])) { - return null; - } - - if (!isset($operands[0][0])) { - throw new LogicException('Incorrect sub-criteria array.'); - } - if (!is_string($operands[0][0])) { - throw new LogicException('Sub-criteria operator must be a string.'); - } - - $subCondition = self::make($operands[0][0], array_slice($operands[0], 1)); + $subCondition = $filterHandler->handle($operands[0]); if (isset($subCondition[0]) && is_string($subCondition[0])) { $convert = [ diff --git a/src/FilterHandler/QueryHandlerInterface.php b/src/FilterHandler/QueryHandlerInterface.php index 016927c..c753b6d 100644 --- a/src/FilterHandler/QueryHandlerInterface.php +++ b/src/FilterHandler/QueryHandlerInterface.php @@ -4,9 +4,10 @@ namespace Yiisoft\Data\Db\FilterHandler; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\FilterHandlerInterface; interface QueryHandlerInterface extends FilterHandlerInterface { - public function getCondition(string $operator, array $operands): ?array; + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array; } From c3f0872b1481bd26ccb2dd9877a0940ef678d7bf Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 18:04:44 +0300 Subject: [PATCH 10/16] refactor --- src/FilterHandler/AllHandler.php | 2 +- src/FilterHandler/AnyHandler.php | 2 +- src/FilterHandler/BaseHandler.php | 15 -- src/FilterHandler/BetweenHandler.php | 24 +- src/FilterHandler/CompareHandler.php | 32 +++ src/FilterHandler/ConditionFactory.php | 220 ------------------ src/FilterHandler/EqualsEmptyHandler.php | 15 +- src/FilterHandler/EqualsHandler.php | 29 ++- src/FilterHandler/EqualsNullHandler.php | 15 +- src/FilterHandler/ExistsHandler.php | 17 +- src/FilterHandler/GreaterThanHandler.php | 2 +- .../GreaterThanOrEqualHandler.php | 2 +- src/FilterHandler/GroupHandler.php | 46 ++++ src/FilterHandler/InHandler.php | 16 +- src/FilterHandler/LessThanHandler.php | 2 +- src/FilterHandler/LessThanOrEqualHandler.php | 2 +- src/FilterHandler/LikeHandler.php | 16 +- src/FilterHandler/NotHandler.php | 38 ++- 18 files changed, 246 insertions(+), 249 deletions(-) delete mode 100644 src/FilterHandler/BaseHandler.php create mode 100644 src/FilterHandler/CompareHandler.php delete mode 100644 src/FilterHandler/ConditionFactory.php create mode 100644 src/FilterHandler/GroupHandler.php diff --git a/src/FilterHandler/AllHandler.php b/src/FilterHandler/AllHandler.php index 8e2fbf7..3f31b01 100644 --- a/src/FilterHandler/AllHandler.php +++ b/src/FilterHandler/AllHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\All; -final class AllHandler extends BaseHandler +final class AllHandler extends GroupHandler { public function getOperator(): string { diff --git a/src/FilterHandler/AnyHandler.php b/src/FilterHandler/AnyHandler.php index f705b09..e8ac314 100644 --- a/src/FilterHandler/AnyHandler.php +++ b/src/FilterHandler/AnyHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\Any; -final class AnyHandler extends BaseHandler +final class AnyHandler extends GroupHandler { public function getOperator(): string { diff --git a/src/FilterHandler/BaseHandler.php b/src/FilterHandler/BaseHandler.php deleted file mode 100644 index 2985c47..0000000 --- a/src/FilterHandler/BaseHandler.php +++ /dev/null @@ -1,15 +0,0 @@ -format('Y-m-d H:i:s') + : $operands[1]; + $to = $operands[2] instanceof DateTimeInterface + ? $operands[2]->format('Y-m-d H:i:s') + : $operands[2]; + return ['BETWEEN', $operands[0], $from, $to]; + } } diff --git a/src/FilterHandler/CompareHandler.php b/src/FilterHandler/CompareHandler.php new file mode 100644 index 0000000..6927352 --- /dev/null +++ b/src/FilterHandler/CompareHandler.php @@ -0,0 +1,32 @@ +format('Y-m-d H:i:s') + : $operands[1]; + + return [$operator, $operands[0], $value]; + } +} diff --git a/src/FilterHandler/ConditionFactory.php b/src/FilterHandler/ConditionFactory.php deleted file mode 100644 index 2ccdb47..0000000 --- a/src/FilterHandler/ConditionFactory.php +++ /dev/null @@ -1,220 +0,0 @@ - self::makeGroup($operator, $operands, $criteriaHandler), - 'not' => self::makeNot($operator, $operands, $criteriaHandler), - 'like' => self::makeLike($operator, $operands), - 'between' => self::makeBetween($operator, $operands), - 'in' => self::makeIn($operator, $operands), - 'empty' => self::makeEmpty($operator, $operands), - 'null' => self::makeNull($operator, $operands), - '=' => self::makeEquals($operator, $operands), - '>', '<', '>=', '<=' => self::makeCompare($operator, $operands), - 'exists' => self::makeExists($operator, $operands), - default => throw new LogicException(sprintf('Not supported operator: %s.', $operator)), - }; - } - - private static function makeGroup(string $operator, array $operands, CriteriaHandler $filterHandler): ?array - { - if (!array_key_exists(0, $operands)) { - throw new LogicException( - sprintf( - 'Not found parameter for the "%s" operator.', - $operator, - ) - ); - } - if (!is_array($operands[0])) { - throw new LogicException( - sprintf( - 'The parameter for "%s" operator must be an array. Got %s.', - $operator, - get_debug_type($operands[0]) - ) - ); - } - if (empty($operands[0])) { - return null; - } - $condition = [strtoupper($operator)]; - foreach ($operands[0] as $subCriteria) { - if (!is_array($subCriteria)) { - throw new LogicException('Incorrect sub-criteria.'); - } - $condition[] = $filterHandler->handle($subCriteria); - } - return $condition; - } - - private static function makeNot(string $operator, array $operands, CriteriaHandler $filterHandler): array - { - if ( - array_keys($operands) !== [0] - || !is_array($operands[0]) - ) { - throw new LogicException('Incorrect criteria for the "not" operator.'); - } - $subCondition = $filterHandler->handle($operands[0]); - - if (isset($subCondition[0]) && is_string($subCondition[0])) { - $convert = [ - 'IS' => 'IS NOT', - 'IN' => 'NOT IN', - 'EXISTS' => 'NOT EXISTS', - 'BETWEEN' => 'NOT BETWEEN', - 'LIKE' => 'NOT LIKE', - 'ILIKE' => 'NOT ILIKE', - '>' => '<=', - '>=' => '<', - '<' => '>=', - '<=' => '>', - '=' => '!=', - ]; - $operator = strtoupper($subCondition[0]); - if (isset($convert[$operator])) { - $subCondition[0] = $convert[$operator]; - return $subCondition; - } - } - - return ['NOT', $subCondition]; - } - - private static function makeLike(string $operator, array $criteria): array - { - if ( - array_keys($criteria) !== [0, 1] - || !is_string($criteria[0]) - || !is_string($criteria[1]) - ) { - throw new LogicException('Incorrect criteria for the "like" operator.'); - } - return ['LIKE', $criteria[0], $criteria[1]]; - } - - private static function makeBetween(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0, 1, 2] - || !is_string($operands[0]) - || !(is_scalar($operands[1]) || $operands[1] instanceof DateTimeInterface) - || !(is_scalar($operands[2]) || $operands[2] instanceof DateTimeInterface) - ) { - throw new LogicException('Incorrect criteria for the "between" operator.'); - } - $from = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - $to = $operands[2] instanceof DateTimeInterface - ? $operands[2]->format('Y-m-d H:i:s') - : $operands[2]; - return ['BETWEEN', $operands[0], $from, $to]; - } - - private static function makeIn(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0, 1] - || !is_string($operands[0]) - || !is_array($operands[1]) - ) { - throw new LogicException('Incorrect criteria for the "in" operator.'); - } - return ['IN', $operands[0], $operands[1]]; - } - - private static function makeEmpty(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0] - || !is_string($operands[0]) - ) { - throw new LogicException('Incorrect criteria for the "empty" operator.'); - } - return ['OR', ['IS', $operands[0], null], ['=', $operands[0], '']]; - } - - private static function makeNull(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0] - || !is_string($operands[0]) - ) { - throw new LogicException('Incorrect criteria for the "empty" operator.'); - } - return ['IS', $operands[0], null]; - } - - private static function makeEquals(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0, 1] - || !is_string($operands[0]) - || ( - !is_string($operands[1]) - && !(is_scalar($operands[1]) || is_null($operands[1]) || $operands[1] instanceof DateTimeInterface) - ) - ) { - throw new LogicException('Incorrect criteria for the "=" operator.'); - } - - if ($operands[1] === null) { - return ['IS NULL', $operands[0]]; - } - - $value = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - - return ['=', $operands[0], $value]; - } - - private static function makeCompare(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0, 1] - || !is_string($operands[0]) - || ( - !is_string($operands[1]) - && !(is_scalar($operands[1]) || $operands[1] instanceof DateTimeInterface) - ) - ) { - throw new LogicException(sprintf('Incorrect criteria for the "%s" operator.', $operator)); - } - - $value = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - - return [$operator, $operands[0], $value]; - } - - private static function makeExists(string $operator, array $operands): array - { - if ( - array_keys($operands) !== [0] - || !$operands[0] instanceof QueryInterface - ) { - throw new LogicException('Incorrect criteria for the "exists" operator.'); - } - - return ['EXISTS', $operands[0]]; - } -} diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index cf17c32..69070cd 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -4,12 +4,25 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\EqualsEmpty; -final class EqualsEmptyHandler extends BaseHandler +final class EqualsEmptyHandler implements QueryHandlerInterface { public function getOperator(): string { return EqualsEmpty::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0] + || !is_string($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "empty" operator.'); + } + return ['OR', ['IS', $operands[0], null], ['=', $operands[0], '']]; + } } diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index 0d587e9..a1d7f25 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -4,12 +4,39 @@ namespace Yiisoft\Data\Db\FilterHandler; +use DateTimeInterface; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Equals; -final class EqualsHandler extends BaseHandler +final class EqualsHandler implements QueryHandlerInterface { public function getOperator(): string { return Equals::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || ( + !is_string($operands[1]) + && !(is_scalar($operands[1]) || is_null($operands[1]) || $operands[1] instanceof DateTimeInterface) + ) + ) { + throw new LogicException('Incorrect criteria for the "=" operator.'); + } + + if ($operands[1] === null) { + return ['IS NULL', $operands[0]]; + } + + $value = $operands[1] instanceof DateTimeInterface + ? $operands[1]->format('Y-m-d H:i:s') + : $operands[1]; + + return ['=', $operands[0], $value]; + } } diff --git a/src/FilterHandler/EqualsNullHandler.php b/src/FilterHandler/EqualsNullHandler.php index 02d0896..2c90de9 100644 --- a/src/FilterHandler/EqualsNullHandler.php +++ b/src/FilterHandler/EqualsNullHandler.php @@ -4,12 +4,25 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\EqualsNull; -final class EqualsNullHandler extends BaseHandler +final class EqualsNullHandler implements QueryHandlerInterface { public function getOperator(): string { return EqualsNull::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0] + || !is_string($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "empty" operator.'); + } + return ['IS', $operands[0], null]; + } } diff --git a/src/FilterHandler/ExistsHandler.php b/src/FilterHandler/ExistsHandler.php index d72b59c..56ff297 100644 --- a/src/FilterHandler/ExistsHandler.php +++ b/src/FilterHandler/ExistsHandler.php @@ -4,12 +4,27 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Db\Filter\Exists; +use Yiisoft\Db\Query\QueryInterface; -final class ExistsHandler extends BaseHandler +final class ExistsHandler implements QueryHandlerInterface { public function getOperator(): string { return Exists::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0] + || !$operands[0] instanceof QueryInterface + ) { + throw new LogicException('Incorrect criteria for the "exists" operator.'); + } + + return ['EXISTS', $operands[0]]; + } } diff --git a/src/FilterHandler/GreaterThanHandler.php b/src/FilterHandler/GreaterThanHandler.php index fca71bb..a2bcf2d 100644 --- a/src/FilterHandler/GreaterThanHandler.php +++ b/src/FilterHandler/GreaterThanHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\GreaterThan; -final class GreaterThanHandler extends BaseHandler +final class GreaterThanHandler extends CompareHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GreaterThanOrEqualHandler.php b/src/FilterHandler/GreaterThanOrEqualHandler.php index 5df36fe..aab9df4 100644 --- a/src/FilterHandler/GreaterThanOrEqualHandler.php +++ b/src/FilterHandler/GreaterThanOrEqualHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual; -final class GreaterThanOrEqualHandler extends BaseHandler +final class GreaterThanOrEqualHandler extends CompareHandler { public function getOperator(): string { diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/GroupHandler.php new file mode 100644 index 0000000..f9920ee --- /dev/null +++ b/src/FilterHandler/GroupHandler.php @@ -0,0 +1,46 @@ +handle($subCriteria); + } + return $condition; + } +} diff --git a/src/FilterHandler/InHandler.php b/src/FilterHandler/InHandler.php index ce258c8..a2e36c8 100644 --- a/src/FilterHandler/InHandler.php +++ b/src/FilterHandler/InHandler.php @@ -4,12 +4,26 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\In; -final class InHandler extends BaseHandler +final class InHandler implements QueryHandlerInterface { public function getOperator(): string { return In::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || !is_array($operands[1]) + ) { + throw new LogicException('Incorrect criteria for the "in" operator.'); + } + return ['IN', $operands[0], $operands[1]]; + } } diff --git a/src/FilterHandler/LessThanHandler.php b/src/FilterHandler/LessThanHandler.php index a4a22f1..87cd96b 100644 --- a/src/FilterHandler/LessThanHandler.php +++ b/src/FilterHandler/LessThanHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\LessThan; -final class LessThanHandler extends BaseHandler +final class LessThanHandler extends CompareHandler { public function getOperator(): string { diff --git a/src/FilterHandler/LessThanOrEqualHandler.php b/src/FilterHandler/LessThanOrEqualHandler.php index 5ec1e3b..4fb2957 100644 --- a/src/FilterHandler/LessThanOrEqualHandler.php +++ b/src/FilterHandler/LessThanOrEqualHandler.php @@ -6,7 +6,7 @@ use Yiisoft\Data\Reader\Filter\LessThanOrEqual; -final class LessThanOrEqualHandler extends BaseHandler +final class LessThanOrEqualHandler extends CompareHandler { public function getOperator(): string { diff --git a/src/FilterHandler/LikeHandler.php b/src/FilterHandler/LikeHandler.php index f1b2508..f7d6edc 100644 --- a/src/FilterHandler/LikeHandler.php +++ b/src/FilterHandler/LikeHandler.php @@ -4,12 +4,26 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Like; -final class LikeHandler extends BaseHandler +final class LikeHandler implements QueryHandlerInterface { public function getOperator(): string { return Like::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0, 1] + || !is_string($operands[0]) + || !is_string($operands[1]) + ) { + throw new LogicException('Incorrect criteria for the "like" operator.'); + } + return ['LIKE', $operands[0], $operands[1]]; + } } diff --git a/src/FilterHandler/NotHandler.php b/src/FilterHandler/NotHandler.php index 224fc78..f805889 100644 --- a/src/FilterHandler/NotHandler.php +++ b/src/FilterHandler/NotHandler.php @@ -4,12 +4,48 @@ namespace Yiisoft\Data\Db\FilterHandler; +use LogicException; +use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Not; -final class NotHandler extends BaseHandler +final class NotHandler implements QueryHandlerInterface { public function getOperator(): string { return Not::getOperator(); } + + public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + { + if ( + array_keys($operands) !== [0] + || !is_array($operands[0]) + ) { + throw new LogicException('Incorrect criteria for the "not" operator.'); + } + $subCondition = $criteriaHandler->handle($operands[0]); + + if (isset($subCondition[0]) && is_string($subCondition[0])) { + $convert = [ + 'IS' => 'IS NOT', + 'IN' => 'NOT IN', + 'EXISTS' => 'NOT EXISTS', + 'BETWEEN' => 'NOT BETWEEN', + 'LIKE' => 'NOT LIKE', + 'ILIKE' => 'NOT ILIKE', + '>' => '<=', + '>=' => '<', + '<' => '>=', + '<=' => '>', + '=' => '!=', + ]; + $operator = strtoupper($subCondition[0]); + if (isset($convert[$operator])) { + $subCondition[0] = $convert[$operator]; + return $subCondition; + } + } + + return ['NOT', $subCondition]; + } } From 0db908491cec0c0d6b3ebf55155f991c94a64dca Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 18:07:05 +0300 Subject: [PATCH 11/16] refactor --- src/CriteriaHandler.php | 2 +- src/FilterHandler/BetweenHandler.php | 2 +- src/FilterHandler/CompareHandler.php | 6 +++--- src/FilterHandler/EqualsEmptyHandler.php | 2 +- src/FilterHandler/EqualsHandler.php | 2 +- src/FilterHandler/EqualsNullHandler.php | 2 +- src/FilterHandler/ExistsHandler.php | 2 +- src/FilterHandler/GroupHandler.php | 8 ++++---- src/FilterHandler/InHandler.php | 2 +- src/FilterHandler/LikeHandler.php | 2 +- src/FilterHandler/NotHandler.php | 2 +- src/FilterHandler/QueryHandlerInterface.php | 2 +- 12 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/CriteriaHandler.php b/src/CriteriaHandler.php index 4f16caa..abaece3 100644 --- a/src/CriteriaHandler.php +++ b/src/CriteriaHandler.php @@ -88,7 +88,7 @@ public function handle(array $criteria): ?array $operands = array_slice($criteria, 1); - return $this->getHandlerByOperator($operator)->getCondition($operator, $operands, $this); + return $this->getHandlerByOperator($operator)->getCondition($operands, $this); } private function getHandlerByOperator(string $operator): QueryHandlerInterface diff --git a/src/FilterHandler/BetweenHandler.php b/src/FilterHandler/BetweenHandler.php index 994ce8d..1a7d720 100644 --- a/src/FilterHandler/BetweenHandler.php +++ b/src/FilterHandler/BetweenHandler.php @@ -16,7 +16,7 @@ public function getOperator(): string return Between::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0, 1, 2] diff --git a/src/FilterHandler/CompareHandler.php b/src/FilterHandler/CompareHandler.php index 6927352..a9b2cee 100644 --- a/src/FilterHandler/CompareHandler.php +++ b/src/FilterHandler/CompareHandler.php @@ -10,7 +10,7 @@ abstract class CompareHandler implements QueryHandlerInterface { - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0, 1] @@ -20,13 +20,13 @@ public function getCondition(string $operator, array $operands, CriteriaHandler && !(is_scalar($operands[1]) || $operands[1] instanceof DateTimeInterface) ) ) { - throw new LogicException(sprintf('Incorrect criteria for the "%s" operator.', $operator)); + throw new LogicException(sprintf('Incorrect criteria for the "%s" operator.', $this->getOperator())); } $value = $operands[1] instanceof DateTimeInterface ? $operands[1]->format('Y-m-d H:i:s') : $operands[1]; - return [$operator, $operands[0], $value]; + return [$this->getOperator(), $operands[0], $value]; } } diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index 69070cd..f7d9f43 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return EqualsEmpty::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index a1d7f25..36ca774 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -16,7 +16,7 @@ public function getOperator(): string return Equals::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0, 1] diff --git a/src/FilterHandler/EqualsNullHandler.php b/src/FilterHandler/EqualsNullHandler.php index 2c90de9..8265f78 100644 --- a/src/FilterHandler/EqualsNullHandler.php +++ b/src/FilterHandler/EqualsNullHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return EqualsNull::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/ExistsHandler.php b/src/FilterHandler/ExistsHandler.php index 56ff297..acde58d 100644 --- a/src/FilterHandler/ExistsHandler.php +++ b/src/FilterHandler/ExistsHandler.php @@ -16,7 +16,7 @@ public function getOperator(): string return Exists::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/GroupHandler.php index f9920ee..1b8a1ea 100644 --- a/src/FilterHandler/GroupHandler.php +++ b/src/FilterHandler/GroupHandler.php @@ -12,13 +12,13 @@ */ abstract class GroupHandler implements QueryHandlerInterface { - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if (!array_key_exists(0, $operands)) { throw new LogicException( sprintf( 'Not found parameter for the "%s" operator.', - $operator, + $this->getOperator(), ) ); } @@ -26,7 +26,7 @@ public function getCondition(string $operator, array $operands, CriteriaHandler throw new LogicException( sprintf( 'The parameter for "%s" operator must be an array. Got %s.', - $operator, + $this->getOperator(), get_debug_type($operands[0]) ) ); @@ -34,7 +34,7 @@ public function getCondition(string $operator, array $operands, CriteriaHandler if (empty($operands[0])) { return null; } - $condition = [strtoupper($operator)]; + $condition = [strtoupper($this->getOperator())]; foreach ($operands[0] as $subCriteria) { if (!is_array($subCriteria)) { throw new LogicException('Incorrect sub-criteria.'); diff --git a/src/FilterHandler/InHandler.php b/src/FilterHandler/InHandler.php index a2e36c8..3f58bfb 100644 --- a/src/FilterHandler/InHandler.php +++ b/src/FilterHandler/InHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return In::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0, 1] diff --git a/src/FilterHandler/LikeHandler.php b/src/FilterHandler/LikeHandler.php index f7d6edc..812099c 100644 --- a/src/FilterHandler/LikeHandler.php +++ b/src/FilterHandler/LikeHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return Like::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0, 1] diff --git a/src/FilterHandler/NotHandler.php b/src/FilterHandler/NotHandler.php index f805889..6b78aa0 100644 --- a/src/FilterHandler/NotHandler.php +++ b/src/FilterHandler/NotHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return Not::getOperator(); } - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/QueryHandlerInterface.php b/src/FilterHandler/QueryHandlerInterface.php index c753b6d..16e1d3b 100644 --- a/src/FilterHandler/QueryHandlerInterface.php +++ b/src/FilterHandler/QueryHandlerInterface.php @@ -9,5 +9,5 @@ interface QueryHandlerInterface extends FilterHandlerInterface { - public function getCondition(string $operator, array $operands, CriteriaHandler $criteriaHandler): ?array; + public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array; } From e56c3dd498023bf23d02b5ea7466d0b7345da98b Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 18:23:33 +0300 Subject: [PATCH 12/16] improve --- src/CriteriaHandler.php | 17 ++++++++-- src/FilterHandler/BetweenHandler.php | 16 ++++----- src/FilterHandler/CompareHandler.php | 10 ++---- src/FilterHandler/Context.php | 32 ++++++++++++++++++ src/FilterHandler/EqualsEmptyHandler.php | 2 +- src/FilterHandler/EqualsHandler.php | 9 ++--- src/FilterHandler/EqualsNullHandler.php | 2 +- src/FilterHandler/ExistsHandler.php | 2 +- src/FilterHandler/GroupHandler.php | 4 +-- src/FilterHandler/InHandler.php | 2 +- src/FilterHandler/LikeHandler.php | 2 +- src/FilterHandler/NotHandler.php | 4 +-- src/FilterHandler/QueryHandlerInterface.php | 3 +- src/ValueNormalizer.php | 37 +++++++++++++++++++++ src/ValueNormalizerInterface.php | 12 +++++++ 15 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 src/FilterHandler/Context.php create mode 100644 src/ValueNormalizer.php create mode 100644 src/ValueNormalizerInterface.php diff --git a/src/CriteriaHandler.php b/src/CriteriaHandler.php index abaece3..77712ac 100644 --- a/src/CriteriaHandler.php +++ b/src/CriteriaHandler.php @@ -8,6 +8,7 @@ use Yiisoft\Data\Db\FilterHandler\AllHandler; use Yiisoft\Data\Db\FilterHandler\AnyHandler; use Yiisoft\Data\Db\FilterHandler\BetweenHandler; +use Yiisoft\Data\Db\FilterHandler\Context; use Yiisoft\Data\Db\FilterHandler\EqualsEmptyHandler; use Yiisoft\Data\Db\FilterHandler\EqualsHandler; use Yiisoft\Data\Db\FilterHandler\EqualsNullHandler; @@ -24,13 +25,22 @@ final class CriteriaHandler { + private Context $context; + /** * @psalm-var array */ private array $handlers; - public function __construct(QueryHandlerInterface ...$handlers) - { + /** + * @param QueryHandlerInterface[]|null $handlers + * @param ValueNormalizerInterface|null $valueNormalizer + * @param $ + */ + public function __construct( + ?array $handlers = null, + ValueNormalizerInterface $valueNormalizer = null, + ) { if (empty($handlers)) { $handlers = [ new AllHandler(), @@ -51,6 +61,7 @@ public function __construct(QueryHandlerInterface ...$handlers) } $this->handlers = $this->prepareHandlers($handlers); + $this->context = new Context($this, $valueNormalizer ?? new ValueNormalizer()); } public function withFilterHandlers(FilterHandlerInterface ...$handlers): self @@ -88,7 +99,7 @@ public function handle(array $criteria): ?array $operands = array_slice($criteria, 1); - return $this->getHandlerByOperator($operator)->getCondition($operands, $this); + return $this->getHandlerByOperator($operator)->getCondition($operands, $this->context); } private function getHandlerByOperator(string $operator): QueryHandlerInterface diff --git a/src/FilterHandler/BetweenHandler.php b/src/FilterHandler/BetweenHandler.php index 1a7d720..3cb6ab2 100644 --- a/src/FilterHandler/BetweenHandler.php +++ b/src/FilterHandler/BetweenHandler.php @@ -6,7 +6,6 @@ use DateTimeInterface; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Between; final class BetweenHandler implements QueryHandlerInterface @@ -16,7 +15,7 @@ public function getOperator(): string return Between::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0, 1, 2] @@ -26,12 +25,11 @@ public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ) { throw new LogicException('Incorrect criteria for the "between" operator.'); } - $from = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - $to = $operands[2] instanceof DateTimeInterface - ? $operands[2]->format('Y-m-d H:i:s') - : $operands[2]; - return ['BETWEEN', $operands[0], $from, $to]; + return [ + 'BETWEEN', + $operands[0], + $context->normalizeValueToScalar($operands[1]), + $context->normalizeValueToScalar($operands[2]), + ]; } } diff --git a/src/FilterHandler/CompareHandler.php b/src/FilterHandler/CompareHandler.php index a9b2cee..963bd4c 100644 --- a/src/FilterHandler/CompareHandler.php +++ b/src/FilterHandler/CompareHandler.php @@ -6,11 +6,10 @@ use DateTimeInterface; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; abstract class CompareHandler implements QueryHandlerInterface { - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0, 1] @@ -22,11 +21,6 @@ public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ) { throw new LogicException(sprintf('Incorrect criteria for the "%s" operator.', $this->getOperator())); } - - $value = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - - return [$this->getOperator(), $operands[0], $value]; + return [$this->getOperator(), $operands[0], $context->normalizeValueToScalar($operands[1])]; } } diff --git a/src/FilterHandler/Context.php b/src/FilterHandler/Context.php new file mode 100644 index 0000000..1abb10c --- /dev/null +++ b/src/FilterHandler/Context.php @@ -0,0 +1,32 @@ +criteriaHandler->handle($criteria); + } + + public function normalizeValueToScalar(mixed $value): bool|string|int|float + { + return $this->valueNormalizer->toScalar($value); + } + + public function normalizeValueToScalarOrNull(mixed $value): bool|string|null|int|float + { + return $this->valueNormalizer->toScalarOrNull($value); + } +} diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index f7d9f43..c3b0be7 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return EqualsEmpty::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index 36ca774..2c2d5a8 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -6,7 +6,6 @@ use DateTimeInterface; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Equals; final class EqualsHandler implements QueryHandlerInterface @@ -16,7 +15,7 @@ public function getOperator(): string return Equals::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0, 1] @@ -33,10 +32,6 @@ public function getCondition(array $operands, CriteriaHandler $criteriaHandler): return ['IS NULL', $operands[0]]; } - $value = $operands[1] instanceof DateTimeInterface - ? $operands[1]->format('Y-m-d H:i:s') - : $operands[1]; - - return ['=', $operands[0], $value]; + return ['=', $operands[0], $context->normalizeValueToScalarOrNull($operands[1])]; } } diff --git a/src/FilterHandler/EqualsNullHandler.php b/src/FilterHandler/EqualsNullHandler.php index 8265f78..54e2abb 100644 --- a/src/FilterHandler/EqualsNullHandler.php +++ b/src/FilterHandler/EqualsNullHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return EqualsNull::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/ExistsHandler.php b/src/FilterHandler/ExistsHandler.php index acde58d..720aa34 100644 --- a/src/FilterHandler/ExistsHandler.php +++ b/src/FilterHandler/ExistsHandler.php @@ -16,7 +16,7 @@ public function getOperator(): string return Exists::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0] diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/GroupHandler.php index 1b8a1ea..37848ff 100644 --- a/src/FilterHandler/GroupHandler.php +++ b/src/FilterHandler/GroupHandler.php @@ -12,7 +12,7 @@ */ abstract class GroupHandler implements QueryHandlerInterface { - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if (!array_key_exists(0, $operands)) { throw new LogicException( @@ -39,7 +39,7 @@ public function getCondition(array $operands, CriteriaHandler $criteriaHandler): if (!is_array($subCriteria)) { throw new LogicException('Incorrect sub-criteria.'); } - $condition[] = $criteriaHandler->handle($subCriteria); + $condition[] = $context->handleCriteria($subCriteria); } return $condition; } diff --git a/src/FilterHandler/InHandler.php b/src/FilterHandler/InHandler.php index 3f58bfb..121cdeb 100644 --- a/src/FilterHandler/InHandler.php +++ b/src/FilterHandler/InHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return In::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0, 1] diff --git a/src/FilterHandler/LikeHandler.php b/src/FilterHandler/LikeHandler.php index 812099c..4769afa 100644 --- a/src/FilterHandler/LikeHandler.php +++ b/src/FilterHandler/LikeHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return Like::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0, 1] diff --git a/src/FilterHandler/NotHandler.php b/src/FilterHandler/NotHandler.php index 6b78aa0..12f2f62 100644 --- a/src/FilterHandler/NotHandler.php +++ b/src/FilterHandler/NotHandler.php @@ -15,7 +15,7 @@ public function getOperator(): string return Not::getOperator(); } - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array + public function getCondition(array $operands, Context $context): ?array { if ( array_keys($operands) !== [0] @@ -23,7 +23,7 @@ public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ) { throw new LogicException('Incorrect criteria for the "not" operator.'); } - $subCondition = $criteriaHandler->handle($operands[0]); + $subCondition = $context->handleCriteria($operands[0]); if (isset($subCondition[0]) && is_string($subCondition[0])) { $convert = [ diff --git a/src/FilterHandler/QueryHandlerInterface.php b/src/FilterHandler/QueryHandlerInterface.php index 16e1d3b..f4c5fa0 100644 --- a/src/FilterHandler/QueryHandlerInterface.php +++ b/src/FilterHandler/QueryHandlerInterface.php @@ -4,10 +4,9 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\FilterHandlerInterface; interface QueryHandlerInterface extends FilterHandlerInterface { - public function getCondition(array $operands, CriteriaHandler $criteriaHandler): ?array; + public function getCondition(array $operands, Context $context): ?array; } diff --git a/src/ValueNormalizer.php b/src/ValueNormalizer.php new file mode 100644 index 0000000..d0bdfc9 --- /dev/null +++ b/src/ValueNormalizer.php @@ -0,0 +1,37 @@ +format($this->dateTimeFormat); + } + + throw new RuntimeException('Invalid value.'); + } + + public function toScalarOrNull(mixed $value): bool|string|null|int|float + { + if ($value === null) { + return null; + } + return $this->toScalar($value); + } +} diff --git a/src/ValueNormalizerInterface.php b/src/ValueNormalizerInterface.php new file mode 100644 index 0000000..7aecf7b --- /dev/null +++ b/src/ValueNormalizerInterface.php @@ -0,0 +1,12 @@ + Date: Thu, 15 Feb 2024 15:26:41 +0000 Subject: [PATCH 13/16] Apply fixes from StyleCI --- src/AbstractQueryDataReader.php | 22 +--------------------- src/CriteriaHandler.php | 2 +- src/FilterHandler/EqualsEmptyHandler.php | 1 - src/FilterHandler/EqualsHandler.php | 2 +- src/FilterHandler/EqualsNullHandler.php | 1 - src/FilterHandler/ExistsHandler.php | 1 - src/FilterHandler/GroupHandler.php | 1 - src/FilterHandler/InHandler.php | 1 - src/FilterHandler/LikeHandler.php | 1 - src/FilterHandler/NotHandler.php | 1 - tests/DataReaderTest.php | 17 ----------------- 11 files changed, 3 insertions(+), 47 deletions(-) diff --git a/src/AbstractQueryDataReader.php b/src/AbstractQueryDataReader.php index 6913c20..cc9235e 100644 --- a/src/AbstractQueryDataReader.php +++ b/src/AbstractQueryDataReader.php @@ -6,23 +6,6 @@ use Generator; use InvalidArgumentException; -use LogicException; -use RuntimeException; -use Yiisoft\Data\Db\FilterHandler\AllHandler; -use Yiisoft\Data\Db\FilterHandler\AnyHandler; -use Yiisoft\Data\Db\FilterHandler\BetweenHandler; -use Yiisoft\Data\Db\FilterHandler\EqualsEmptyHandler; -use Yiisoft\Data\Db\FilterHandler\EqualsHandler; -use Yiisoft\Data\Db\FilterHandler\ExistsHandler; -use Yiisoft\Data\Db\FilterHandler\GreaterThanHandler; -use Yiisoft\Data\Db\FilterHandler\GreaterThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\InHandler; -use Yiisoft\Data\Db\FilterHandler\EqualsNullHandler; -use Yiisoft\Data\Db\FilterHandler\LessThanHandler; -use Yiisoft\Data\Db\FilterHandler\LessThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\LikeHandler; -use Yiisoft\Data\Db\FilterHandler\NotHandler; -use Yiisoft\Data\Db\FilterHandler\QueryHandlerInterface; use Yiisoft\Data\Reader\FilterHandlerInterface; use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Data\Reader\Sort; @@ -30,8 +13,6 @@ use function array_key_first; use function is_array; -use function is_object; -use function sprintf; /** * @template TKey as array-key @@ -65,8 +46,7 @@ abstract class AbstractQueryDataReader implements QueryDataReaderInterface public function __construct( private QueryInterface $query, ?CriteriaHandler $filterHandler = null, - ) - { + ) { $this->criteriaHandler = $filterHandler ?? new CriteriaHandler(); } diff --git a/src/CriteriaHandler.php b/src/CriteriaHandler.php index 77712ac..f4d6cf3 100644 --- a/src/CriteriaHandler.php +++ b/src/CriteriaHandler.php @@ -56,7 +56,7 @@ public function __construct( new NotHandler(), new BetweenHandler(), new EqualsNullHandler(), - new EqualsEmptyHandler() + new EqualsEmptyHandler(), ]; } diff --git a/src/FilterHandler/EqualsEmptyHandler.php b/src/FilterHandler/EqualsEmptyHandler.php index c3b0be7..0cdb150 100644 --- a/src/FilterHandler/EqualsEmptyHandler.php +++ b/src/FilterHandler/EqualsEmptyHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\EqualsEmpty; final class EqualsEmptyHandler implements QueryHandlerInterface diff --git a/src/FilterHandler/EqualsHandler.php b/src/FilterHandler/EqualsHandler.php index 2c2d5a8..0bbeebd 100644 --- a/src/FilterHandler/EqualsHandler.php +++ b/src/FilterHandler/EqualsHandler.php @@ -22,7 +22,7 @@ public function getCondition(array $operands, Context $context): ?array || !is_string($operands[0]) || ( !is_string($operands[1]) - && !(is_scalar($operands[1]) || is_null($operands[1]) || $operands[1] instanceof DateTimeInterface) + && !(is_scalar($operands[1]) || null === $operands[1] || $operands[1] instanceof DateTimeInterface) ) ) { throw new LogicException('Incorrect criteria for the "=" operator.'); diff --git a/src/FilterHandler/EqualsNullHandler.php b/src/FilterHandler/EqualsNullHandler.php index 54e2abb..96b1fe8 100644 --- a/src/FilterHandler/EqualsNullHandler.php +++ b/src/FilterHandler/EqualsNullHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\EqualsNull; final class EqualsNullHandler implements QueryHandlerInterface diff --git a/src/FilterHandler/ExistsHandler.php b/src/FilterHandler/ExistsHandler.php index 720aa34..aab64ac 100644 --- a/src/FilterHandler/ExistsHandler.php +++ b/src/FilterHandler/ExistsHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Db\Filter\Exists; use Yiisoft\Db\Query\QueryInterface; diff --git a/src/FilterHandler/GroupHandler.php b/src/FilterHandler/GroupHandler.php index 37848ff..6b8847e 100644 --- a/src/FilterHandler/GroupHandler.php +++ b/src/FilterHandler/GroupHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; /** * @internal diff --git a/src/FilterHandler/InHandler.php b/src/FilterHandler/InHandler.php index 121cdeb..0ee8a2e 100644 --- a/src/FilterHandler/InHandler.php +++ b/src/FilterHandler/InHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\In; final class InHandler implements QueryHandlerInterface diff --git a/src/FilterHandler/LikeHandler.php b/src/FilterHandler/LikeHandler.php index 4769afa..3dfb77e 100644 --- a/src/FilterHandler/LikeHandler.php +++ b/src/FilterHandler/LikeHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Like; final class LikeHandler implements QueryHandlerInterface diff --git a/src/FilterHandler/NotHandler.php b/src/FilterHandler/NotHandler.php index 12f2f62..91147b6 100644 --- a/src/FilterHandler/NotHandler.php +++ b/src/FilterHandler/NotHandler.php @@ -5,7 +5,6 @@ namespace Yiisoft\Data\Db\FilterHandler; use LogicException; -use Yiisoft\Data\Db\CriteriaHandler; use Yiisoft\Data\Reader\Filter\Not; final class NotHandler implements QueryHandlerInterface diff --git a/tests/DataReaderTest.php b/tests/DataReaderTest.php index ac01c52..f2a9751 100644 --- a/tests/DataReaderTest.php +++ b/tests/DataReaderTest.php @@ -6,28 +6,11 @@ use PHPUnit\Framework\TestCase; use stdClass; -use Yiisoft\Data\Db\FilterHandler\BetweenHandler; -use Yiisoft\Data\Db\FilterHandler\EqualsHandler; -use Yiisoft\Data\Db\FilterHandler\GreaterThanHandler; -use Yiisoft\Data\Db\FilterHandler\GreaterThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\InHandler; -use Yiisoft\Data\Db\FilterHandler\LessThanHandler; -use Yiisoft\Data\Db\FilterHandler\LessThanOrEqualHandler; -use Yiisoft\Data\Db\FilterHandler\LikeHandler; use Yiisoft\Data\Db\QueryDataReader; use Yiisoft\Data\Db\Tests\Support\CustomerDataReader; use Yiisoft\Data\Db\Tests\Support\CustomerDTO; use Yiisoft\Data\Db\Tests\Support\CustomerQuery; use Yiisoft\Data\Db\Tests\Support\TestTrait; -use Yiisoft\Data\Reader\Filter\Between; -use Yiisoft\Data\Reader\Filter\Equals; -use Yiisoft\Data\Reader\Filter\GreaterThan; -use Yiisoft\Data\Reader\Filter\GreaterThanOrEqual; -use Yiisoft\Data\Reader\Filter\In; -use Yiisoft\Data\Reader\Filter\LessThan; -use Yiisoft\Data\Reader\Filter\LessThanOrEqual; -use Yiisoft\Data\Reader\Filter\Like; -use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Data\Reader\Sort; use Yiisoft\Db\Expression\Expression; use Yiisoft\Db\Query\Query; From 2a3b03a0a9a899dc9279d7aeb2aba493a3156327 Mon Sep 17 00:00:00 2001 From: vjik Date: Thu, 15 Feb 2024 15:26:59 +0000 Subject: [PATCH 14/16] Apply Rector changes (CI) --- src/ValueNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ValueNormalizer.php b/src/ValueNormalizer.php index d0bdfc9..211f5c3 100644 --- a/src/ValueNormalizer.php +++ b/src/ValueNormalizer.php @@ -10,7 +10,7 @@ final class ValueNormalizer implements ValueNormalizerInterface { public function __construct( - private string $dateTimeFormat = 'Y-m-d H:i:s', + private readonly string $dateTimeFormat = 'Y-m-d H:i:s', ) { } From d67ba563ec30a57191e55b9ed5d9d1b6d4bbbbd7 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Thu, 15 Feb 2024 20:14:54 +0300 Subject: [PATCH 15/16] phpdoc --- src/CriteriaHandler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CriteriaHandler.php b/src/CriteriaHandler.php index f4d6cf3..f81ec90 100644 --- a/src/CriteriaHandler.php +++ b/src/CriteriaHandler.php @@ -22,7 +22,12 @@ use Yiisoft\Data\Db\FilterHandler\NotHandler; use Yiisoft\Data\Db\FilterHandler\QueryHandlerInterface; use Yiisoft\Data\Reader\FilterHandlerInterface; +use Yiisoft\Db\Query\QueryPartsInterface; +/** + * `CriteriaHandler` process filter criteria array from {@see FilterInterface::toCriteriaArray()} to condition array + * that used in {@see QueryPartsInterface::andWhere()} and {@see QueryPartsInterface::andHaving()}. + */ final class CriteriaHandler { private Context $context; @@ -35,7 +40,6 @@ final class CriteriaHandler /** * @param QueryHandlerInterface[]|null $handlers * @param ValueNormalizerInterface|null $valueNormalizer - * @param $ */ public function __construct( ?array $handlers = null, From 4809681a4e71b0bb628a99318c5662a1d6955570 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 16 Feb 2024 09:16:55 +0300 Subject: [PATCH 16/16] Update src/CriteriaHandler.php Co-authored-by: Alexander Makarov --- src/CriteriaHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CriteriaHandler.php b/src/CriteriaHandler.php index f81ec90..cbaa665 100644 --- a/src/CriteriaHandler.php +++ b/src/CriteriaHandler.php @@ -25,8 +25,8 @@ use Yiisoft\Db\Query\QueryPartsInterface; /** - * `CriteriaHandler` process filter criteria array from {@see FilterInterface::toCriteriaArray()} to condition array - * that used in {@see QueryPartsInterface::andWhere()} and {@see QueryPartsInterface::andHaving()}. + * `CriteriaHandler` processes filter criteria array from {@see FilterInterface::toCriteriaArray()} into condition array + * that is used in {@see QueryPartsInterface::andWhere()} and {@see QueryPartsInterface::andHaving()}. */ final class CriteriaHandler {