Skip to content
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

Big refactoring + Add support filters from Yii Data #43

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 @@
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 @@ -129,16 +86,16 @@
if ($this->count === null) {
$q = $this->countParam ?? '*';

if ($q === '*' && is_array($this->data) && !$this->limit && !$this->offset) {

Check warning on line 89 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "Identical": --- Original +++ New @@ @@ { if ($this->count === null) { $q = $this->countParam ?? '*'; - if ($q === '*' && is_array($this->data) && !$this->limit && !$this->offset) { + if ($q !== '*' && is_array($this->data) && !$this->limit && !$this->offset) { $this->count = count($this->data); } else { $query = $this->getPreparedQuery();

Check warning on line 89 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "LogicalNot": --- Original +++ New @@ @@ { if ($this->count === null) { $q = $this->countParam ?? '*'; - if ($q === '*' && is_array($this->data) && !$this->limit && !$this->offset) { + if ($q === '*' && is_array($this->data) && $this->limit && !$this->offset) { $this->count = count($this->data); } else { $query = $this->getPreparedQuery();

Check warning on line 89 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "LogicalNot": --- Original +++ New @@ @@ { if ($this->count === null) { $q = $this->countParam ?? '*'; - if ($q === '*' && is_array($this->data) && !$this->limit && !$this->offset) { + if ($q === '*' && is_array($this->data) && !$this->limit && $this->offset) { $this->count = count($this->data); } else { $query = $this->getPreparedQuery();
$this->count = count($this->data);
} else {
$query = $this->getPreparedQuery();
$query->offset(null);

Check warning on line 93 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $this->count = count($this->data); } else { $query = $this->getPreparedQuery(); - $query->offset(null); + $query->limit(null); $query->orderBy(''); /** @psalm-var non-negative-int */
$query->limit(null);

Check warning on line 94 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ } else { $query = $this->getPreparedQuery(); $query->offset(null); - $query->limit(null); + $query->orderBy(''); /** @psalm-var non-negative-int */ $this->count = (int) $query->count($q);
$query->orderBy('');

Check warning on line 95 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $query = $this->getPreparedQuery(); $query->offset(null); $query->limit(null); - $query->orderBy(''); + /** @psalm-var non-negative-int */ $this->count = (int) $query->count($q); }

/** @psalm-var non-negative-int */
$this->count = (int) $query->count($q);

Check warning on line 98 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "CastInt": --- Original +++ New @@ @@ $query->limit(null); $query->orderBy(''); /** @psalm-var non-negative-int */ - $this->count = (int) $query->count($q); + $this->count = $query->count($q); } } return $this->count;
}
}

Expand All @@ -147,15 +104,15 @@

public function getPreparedQuery(): QueryInterface
{
$query = $this->applyFilter(clone $this->query);

Check warning on line 107 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "CloneRemoval": --- Original +++ New @@ @@ } public function getPreparedQuery() : QueryInterface { - $query = $this->applyFilter(clone $this->query); + $query = $this->applyFilter($this->query); $query = $this->applyHaving($query); if ($this->limit) { $query->limit($this->limit);
$query = $this->applyHaving($query);

if ($this->limit) {
$query->limit($this->limit);

Check warning on line 111 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $query = $this->applyFilter(clone $this->query); $query = $this->applyHaving($query); if ($this->limit) { - $query->limit($this->limit); + } if ($this->offset) { $query->offset($this->offset);
}

if ($this->offset) {
$query->offset($this->offset);

Check warning on line 115 in src/AbstractQueryDataReader.php

View workflow job for this annotation

GitHub Actions / mutation / PHP 8.3-ubuntu-latest

Escaped Mutant for Mutator "MethodCallRemoval": --- Original +++ New @@ @@ $query->limit($this->limit); } if ($this->offset) { - $query->offset($this->offset); + } if ($criteria = $this->sort?->getCriteria()) { $query->addOrderBy($criteria);
}

if ($criteria = $this->sort?->getCriteria()) {
Expand All @@ -165,24 +122,13 @@
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 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 @@
{
$new = clone $this;
$new->count = $new->data = null;
$new->filterHandlers = array_merge(
$this->filterHandlers,
$this->prepareHandlers(...$filterHandlers)
);

$new->criteriaHandler = $this->criteriaHandler->withFilterHandlers(...$filterHandlers);

Check warning on line 262 in src/AbstractQueryDataReader.php

View check run for this annotation

Codecov / codecov/patch

src/AbstractQueryDataReader.php#L262

Added line #L262 was not covered by tests
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
samdark marked this conversation as resolved.
Show resolved Hide resolved
{
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

Check warning on line 71 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L71

Added line #L71 was not covered by tests
{
foreach ($handlers as $handler) {
if (!$handler instanceof QueryHandlerInterface) {
throw new LogicException(
sprintf(
'Filter handler must implement "%s".',
QueryHandlerInterface::class,
)
);

Check warning on line 80 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L73-L80

Added lines #L73 - L80 were not covered by tests
}
}
/** @var QueryHandlerInterface[] $handlers */

$new = clone $this;
$new->handlers = array_merge(
$this->handlers,
$this->prepareHandlers($handlers),
);
return $new;

Check warning on line 90 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L85-L90

Added lines #L85 - L90 were not covered by tests
}

public function handle(array $criteria): ?array
{
if (!isset($criteria[0])) {
throw new LogicException('Incorrect criteria array.');

Check warning on line 96 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L96

Added line #L96 was not covered by tests
}

$operator = $criteria[0];
if (!is_string($operator)) {
throw new LogicException('Criteria operator must be a string.');

Check warning on line 101 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L101

Added line #L101 was not covered by tests
}

$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));

Check warning on line 112 in src/CriteriaHandler.php

View check run for this annotation

Codecov / codecov/patch

src/CriteriaHandler.php#L112

Added line #L112 was not covered by tests
}

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
Loading