Skip to content

Big refactoring + Add support filters from Yii Data #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Feb 18, 2024
103 changes: 16 additions & 87 deletions src/AbstractQueryDataReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,13 @@

use Generator;
use InvalidArgumentException;
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\ILikeHandler;
use Yiisoft\Data\Db\FilterHandler\InHandler;
use Yiisoft\Data\Db\FilterHandler\IsNullHandler;
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;
use Yiisoft\Data\Reader\Sort;
use Yiisoft\Db\Query\QueryInterface;

use function array_key_first;
use function is_array;
use function is_object;
use function sprintf;

/**
* @template TKey as array-key
Expand All @@ -44,6 +22,8 @@
*/
abstract class AbstractQueryDataReader implements QueryDataReaderInterface
{
private CriteriaHandler $criteriaHandler;

private ?Sort $sort = null;
private ?FilterInterface $filter = null;
private ?FilterInterface $having = null;
Expand All @@ -63,34 +43,11 @@ abstract class AbstractQueryDataReader implements QueryDataReaderInterface
private ?int $batchSize = 100;
private ?string $countParam = null;

/**
* @var QueryHandlerInterface[]
* @psalm-var array<string, QueryHandlerInterface>
*/
protected array $filterHandlers = [];

public function __construct(private QueryInterface $query)
{
$this->filterHandlers = $this->prepareHandlers(
new AllHandler(),
new AnyHandler(),
new EqualsHandler(),
new GreaterThanHandler(),
new GreaterThanOrEqualHandler(),
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 EqualsEmptyHandler()
);
public function __construct(
private QueryInterface $query,
?CriteriaHandler $filterHandler = null,
) {
$this->criteriaHandler = $filterHandler ?? new CriteriaHandler();
}

/**
Expand Down Expand Up @@ -165,24 +122,13 @@ 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) {
$query = $this->getHandlerByOperation($this->filter)
->applyFilter($query, $this->filter);
$condition = $this->criteriaHandler->handle($this->filter->toCriteriaArray());
if ($condition !== null) {
$query = $query->andWhere($condition);
}
}

return $query;
Expand All @@ -191,8 +137,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->criteriaHandler->handle($this->having->toCriteriaArray());
if ($condition !== null) {
$query = $query->andHaving($condition);
}
}

return $query;
Expand Down Expand Up @@ -311,29 +259,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->criteriaHandler = $this->criteriaHandler->withFilterHandlers(...$filterHandlers);
return $new;
}

/**
* @return QueryHandlerInterface[]
* @psalm-return array<string, QueryHandlerInterface>
*/
private function prepareHandlers(QueryHandlerInterface ...$queryHandlers): array
{
$handlers = [];

foreach ($queryHandlers as $handler) {
$handlers[$handler->getOperator()] = $handler;
}

return $handlers;
}

public function getSort(): ?Sort
{
return $this->sort;
Expand Down
31 changes: 0 additions & 31 deletions src/ColumnFormatterTrait.php

This file was deleted.

132 changes: 132 additions & 0 deletions src/CriteriaHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Data\Db;

use LogicException;
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;
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\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\Db\Query\QueryPartsInterface;

/**
* `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
{
private Context $context;

/**
* @psalm-var array<string, QueryHandlerInterface>
*/
private array $handlers;

/**
* @param QueryHandlerInterface[]|null $handlers
* @param ValueNormalizerInterface|null $valueNormalizer
*/
public function __construct(
?array $handlers = null,
ValueNormalizerInterface $valueNormalizer = null,
) {
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);
$this->context = new Context($this, $valueNormalizer ?? new ValueNormalizer());
}

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(array $criteria): ?array
{
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($operands, $this->context);
}

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<string, QueryHandlerInterface>
*/
private function prepareHandlers(array $handlers): array
{
$result = [];
foreach ($handlers as $handler) {
$result[$handler->getOperator()] = $handler;
}
return $result;
}
}
15 changes: 0 additions & 15 deletions src/Filter/All.php

This file was deleted.

15 changes: 0 additions & 15 deletions src/Filter/Any.php

This file was deleted.

Loading