diff --git a/src/FilterHandler.php b/src/FilterHandler.php index dedf14c..24fae6d 100644 --- a/src/FilterHandler.php +++ b/src/FilterHandler.php @@ -22,7 +22,7 @@ */ final class FilterHandler { - private Context $context; + private readonly Context $context; /** * @psalm-var array @@ -32,25 +32,12 @@ final class FilterHandler /** * @psalm-param list $handlers */ - public function __construct( - array $handlers, - private readonly FieldMapperInterface $fieldMapper, - ) { + public function __construct(array $handlers, FieldMapperInterface $fieldMapper) + { $this->handlers = $this->prepareHandlers($handlers); $this->context = new Context($this, $fieldMapper); } - public function withAddedFilterHandlers(QueryFilterHandlerInterface ...$handlers): self - { - $new = clone $this; - $new->handlers = array_merge( - $this->handlers, - $this->prepareHandlers($handlers), - ); - $new->context = new Context($new, $this->fieldMapper); - return $new; - } - public function handle(FilterInterface $filter): ConditionInterface { return $this->getHandlerByOperator($filter::class)->getCondition($filter, $this->context); diff --git a/src/FilterHandler/QueryFilterHandlerInterface.php b/src/FilterHandler/QueryFilterHandlerInterface.php index b62f0a7..68171c5 100644 --- a/src/FilterHandler/QueryFilterHandlerInterface.php +++ b/src/FilterHandler/QueryFilterHandlerInterface.php @@ -4,11 +4,21 @@ namespace Yiisoft\Data\Db\FilterHandler; -use Yiisoft\Data\Reader\FilterHandlerInterface; use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Db\QueryBuilder\Condition\ConditionInterface; -interface QueryFilterHandlerInterface extends FilterHandlerInterface +interface QueryFilterHandlerInterface { public function getCondition(FilterInterface $filter, Context $context): ConditionInterface; + + /** + * Get matching filter class name. + * + * If the filter is active, a corresponding handler will be used during matching. + * + * @return string The filter class name. + * + * @psalm-return class-string + */ + public function getFilterClass(): string; } diff --git a/src/QueryDataReader.php b/src/QueryDataReader.php index bac355b..c29cc00 100644 --- a/src/QueryDataReader.php +++ b/src/QueryDataReader.php @@ -6,7 +6,6 @@ use Generator; use InvalidArgumentException; -use LogicException; use RuntimeException; use Yiisoft\Data\Db\FieldMapper\ArrayFieldMapper; use Yiisoft\Data\Db\FieldMapper\FieldMapperInterface; @@ -28,7 +27,6 @@ use Yiisoft\Data\Db\FilterHandler\OrXHandler; use Yiisoft\Data\Db\FilterHandler\QueryFilterHandlerInterface; use Yiisoft\Data\Reader\Filter\All; -use Yiisoft\Data\Reader\FilterHandlerInterface; use Yiisoft\Data\Reader\FilterInterface; use Yiisoft\Data\Reader\Sort; use Yiisoft\Db\Expression\CompositeExpression; @@ -67,7 +65,7 @@ class QueryDataReader implements QueryDataReaderInterface /** * @psalm-param non-negative-int|null $limit - * @psalm-param list|null $filterHandlers + * @psalm-param list $extraFilterHandlers * @psalm-param array|FieldMapperInterface $fieldMapper */ public function __construct( @@ -79,13 +77,33 @@ public function __construct( private FilterInterface $filter = new All(), private FilterInterface $having = new All(), private ?int $batchSize = null, - ?array $filterHandlers = null, + array $extraFilterHandlers = [], array|FieldMapperInterface $fieldMapper = [], ) { $this->fieldMapper = is_array($fieldMapper) ? new ArrayFieldMapper($fieldMapper) : $fieldMapper; - $filterHandlers ??= $this->getDefaultFilterHandlers(); - $this->filterHandler = new FilterHandler($filterHandlers, $this->fieldMapper); + $this->filterHandler = new FilterHandler( + [ + new AllHandler(), + new NoneHandler(), + new AndXHandler(), + new OrXHandler(), + new EqualsHandler(), + new GreaterThanHandler(), + new GreaterThanOrEqualHandler(), + new LessThanHandler(), + new LessThanOrEqualHandler(), + new LikeHandler(), + new InHandler(), + new ExistsHandler(), + new NotHandler(), + new BetweenHandler(), + new EqualsNullHandler(), + new EqualsExpressionHandler(), + ...$extraFilterHandlers, + ], + $this->fieldMapper, + ); } /** @@ -98,6 +116,7 @@ final public function getIterator(): Generator /** @psalm-var array $data */ yield from $data; } + /** @infection-ignore-all */ return; } @@ -296,31 +315,6 @@ final public function withBatchSize(?int $batchSize): static return $new; } - /** - * @return static The new instance with the specified filter handlers added. - * - * @psalm-return static - */ - final public function withAddedFilterHandlers(FilterHandlerInterface ...$filterHandlers): static - { - foreach ($filterHandlers as $handler) { - if (!$handler instanceof QueryFilterHandlerInterface) { - throw new LogicException( - sprintf( - 'Filter handler must implement "%s".', - QueryFilterHandlerInterface::class, - ), - ); - } - } - /** @var QueryFilterHandlerInterface[] $filterHandlers */ - - $new = clone $this; - $new->count = $new->cache = null; - $new->filterHandler = $this->filterHandler->withAddedFilterHandlers(...$filterHandlers); - return $new; - } - final public function getSort(): ?Sort { return $this->sort; @@ -341,31 +335,6 @@ final public function getOffset(): int return $this->offset; } - /** - * @psalm-return list - */ - private function getDefaultFilterHandlers(): array - { - return [ - new AllHandler(), - new NoneHandler(), - new AndXHandler(), - new OrXHandler(), - new EqualsHandler(), - new GreaterThanHandler(), - new GreaterThanOrEqualHandler(), - new LessThanHandler(), - new LessThanOrEqualHandler(), - new LikeHandler(), - new InHandler(), - new ExistsHandler(), - new NotHandler(), - new BetweenHandler(), - new EqualsNullHandler(), - new EqualsExpressionHandler(), - ]; - } - private function convertSortToOrderBy(Sort $sort): array { $result = []; diff --git a/tests/Sqlite/FilterHandlerTest.php b/tests/Sqlite/FilterHandlerTest.php deleted file mode 100644 index 41cb397..0000000 --- a/tests/Sqlite/FilterHandlerTest.php +++ /dev/null @@ -1,41 +0,0 @@ -withAddedFilterHandlers($handler2, $handler3); - - $this->assertNotSame($handler, $handlerWithAdded); - - $this->assertInstanceOf(ConditionInterface::class, $handlerWithAdded->handle(new Equals('field', 'value'))); - $this->assertInstanceOf(ConditionInterface::class, $handlerWithAdded->handle(new None())); - $this->assertInstanceOf(ConditionInterface::class, $handlerWithAdded->handle(new All())); - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Operator "' . In::class . '" is not supported.'); - $handlerWithAdded->handle(new In('field', [])); - } -} diff --git a/tests/Sqlite/QueryDataReaderTest.php b/tests/Sqlite/QueryDataReaderTest.php index 889bfb4..fa0158c 100644 --- a/tests/Sqlite/QueryDataReaderTest.php +++ b/tests/Sqlite/QueryDataReaderTest.php @@ -5,64 +5,16 @@ namespace Yiisoft\Data\Db\Tests\Sqlite; use InvalidArgumentException; -use LogicException; use PHPUnit\Framework\TestCase; -use Yiisoft\Data\Db\FilterHandler\AllHandler; -use Yiisoft\Data\Db\FilterHandler\AndXHandler; -use Yiisoft\Data\Db\FilterHandler\NoneHandler; -use Yiisoft\Data\Db\FilterHandler\QueryFilterHandlerInterface; use Yiisoft\Data\Db\QueryDataReader; use Yiisoft\Data\Db\Tests\TestHelper; use Yiisoft\Data\Reader\Filter\All; -use Yiisoft\Data\Reader\Filter\AndX; -use Yiisoft\Data\Reader\Filter\Equals; use Yiisoft\Data\Reader\Filter\None; use Yiisoft\Data\Reader\Sort; use Yiisoft\Test\Support\Log\SimpleLogger; final class QueryDataReaderTest extends TestCase { - public function testWithAddedFilterHandlers(): void - { - $db = TestHelper::createSqliteConnection(); - $columnBuilder = $db->getColumnBuilderClass(); - $db->createCommand()->createTable('test', ['id' => $columnBuilder::text()])->execute(); - - $handler1 = new AllHandler(); - $handler2 = new NoneHandler(); - $handler3 = new AndXHandler(); - - $dataReader = new QueryDataReader( - $db->createQuery()->from('test'), - filterHandlers: [$handler1], - ); - - $dataReaderWithAdded = $dataReader->withAddedFilterHandlers($handler2, $handler3); - - $this->assertNotSame($dataReader, $dataReaderWithAdded); - - $dataReaderWithFilters = $dataReaderWithAdded->withFilter(new AndX(new All(), new None())); - $dataReaderWithFilters->read(); // No errors - - $dataReaderWithUnsupportedFilter = $dataReaderWithAdded->withFilter(new Equals('id', 'test')); - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Operator "' . Equals::class . '" is not supported.'); - iterator_to_array($dataReaderWithUnsupportedFilter->read()); - } - - public function testWithAddedFilterHandlersWithIncorrectHandler(): void - { - $dataReader = new QueryDataReader( - TestHelper::createSqliteConnection()->createQuery(), - ); - - $iterableHandler = new \Yiisoft\Data\Reader\Iterable\FilterHandler\AllHandler(); - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Filter handler must implement "' . QueryFilterHandlerInterface::class . '".'); - $dataReader->withAddedFilterHandlers($iterableHandler); - } - public function testGetIteratorAfterRead(): void { $data = [['id' => '1'], ['id' => '2']];