Skip to content

Commit

Permalink
collection: unify filtering processing to single pass
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Nov 23, 2021
1 parent f7d70f5 commit e1a1204
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 70 deletions.
84 changes: 52 additions & 32 deletions src/Collection/DbalCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ class DbalCollection implements ICollection
/** @var QueryBuilder */
protected $queryBuilder;

/** @var array<mixed> FindBy expressions for deferred processing */
protected $filtering = [];

/** @var array<mixed> OrderBy expressions for deferred processing */
protected $ordering = [];

/** @var DbalQueryBuilderHelper */
protected $helper;

Expand Down Expand Up @@ -117,44 +123,32 @@ public function getByIdChecked($id): IEntity
public function findBy(array $conds): ICollection
{
$collection = clone $this;
$expression = $collection->getHelper()->processFilterFunction($collection->queryBuilder, $conds, null);
$expression = $expression->applyAggregator($collection->queryBuilder);

foreach ($expression->getUniqueJoins($collection->queryBuilder) as $join) {
$join->applyJoin($collection->queryBuilder);
}

if ($expression->isHavingClause) {
$collection->queryBuilder->andHaving(...$expression->args);
} else {
$collection->queryBuilder->andWhere(...$expression->args);
}
$collection->filtering[] = $conds;
return $collection;
}


public function orderBy($expression, string $direction = ICollection::ASC): ICollection
{
$collection = clone $this;
$helper = $collection->getHelper();
if (is_array($expression) && !isset($expression[0])) {
/** @phpstan-var array<string, string> $expression */
$expression = $expression; // no-op for PHPStan

foreach ($expression as $subExpression => $subDirection) {
$collection->getHelper()->processOrder(
$collection->queryBuilder,
$subExpression,
$subDirection
);
$collection->ordering[] = [
$helper->processPropertyExpr($collection->queryBuilder, $subExpression),
$subDirection,
];
}
} else {
/** @phpstan-var string|list<mixed> $expression */
$expression = $expression; // no-op for PHPStan
$collection->getHelper()->processOrder(
$collection->queryBuilder,
$expression,
$direction
);
$collection->ordering[] = [
$helper->processPropertyExpr($collection->queryBuilder, $expression),
$direction,
];
}
return $collection;
}
Expand All @@ -163,7 +157,7 @@ public function orderBy($expression, string $direction = ICollection::ASC): ICol
public function resetOrderBy(): ICollection
{
$collection = clone $this;
$collection->queryBuilder->orderBy(null);
$collection->getQueryBuilder()->orderBy(null);
return $collection;
}

Expand Down Expand Up @@ -313,19 +307,50 @@ public function __clone()


/**
* @return QueryBuilder
* @internal
*/
public function getQueryBuilder()
public function getQueryBuilder(): QueryBuilder
{
$joins = [];
$helper = $this->getHelper();
$args = $this->filtering;

if (count($args) > 0) {
array_unshift($args, ICollection::AND);
$expression = $helper->processFilterFunction(
$this->queryBuilder,
$args,
null
);
$joins = $expression->joins;
if ($expression->isHavingClause) {
$this->queryBuilder->andHaving(...$expression->args);
} else {
$this->queryBuilder->andWhere(...$expression->args);
}
$this->filtering = [];
}

foreach ($this->ordering as [$expression, $direction]) {
$joins = array_merge($joins, $expression->joins);
$orderingExpression = $helper->processOrderDirection($expression, $direction);
$this->queryBuilder->addOrderBy('%ex', $orderingExpression);
}
$this->ordering = [];

$mergedJoins = $helper->mergeJoins('%and', $joins);
foreach ($mergedJoins as $join) {
$join->applyJoin($this->queryBuilder);
}

return $this->queryBuilder;
}


protected function getIteratorCount(): int
{
if ($this->resultCount === null) {
$builder = clone $this->queryBuilder;
$builder = clone $this->getQueryBuilder();
if (!$builder->hasLimitOffsetClause()) {
$builder->orderBy(null);
}
Expand All @@ -349,12 +374,7 @@ protected function getIteratorCount(): int

protected function execute(): void
{
$builder = clone $this->queryBuilder;

$result = $this->connection->queryArgs(
$builder->getQuerySql(),
$builder->getQueryParameters()
);
$result = $this->connection->queryByQueryBuilder($this->getQueryBuilder());

$this->result = [];
while ($data = $result->fetch()) {
Expand Down
22 changes: 0 additions & 22 deletions src/Collection/Helpers/DbalExpressionResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,4 @@ public function applyAggregator(QueryBuilder $queryBuilder): DbalExpressionResul

return $this->aggregator->aggregate($queryBuilder, $this);
}


/**
* @return array<DbalJoinEntry>
*/
public function getUniqueJoins(QueryBuilder $queryBuilder): array
{
$known = [];
foreach ($queryBuilder->getClause('join')[0] ?? [] as $join) {
$known[$join['table'] . $join['on']] = true; // $from$on
}
$missing = [];
foreach ($this->joins as $join) {
$key = "$join->toExpression AS [$join->toAlias]$join->onExpression";
if (isset($known[$key])) {
continue;
}
$known[$key] = true;
$missing[] = $join;
}
return $missing;
}
}
17 changes: 1 addition & 16 deletions src/Collection/Helpers/DbalQueryBuilderHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,25 +126,10 @@ public function processFilterFunction(
}


/**
* @param string|array $expr
* @phpstan-param string|list<mixed> $expr
*/
public function processOrder(QueryBuilder $builder, $expr, string $direction): void
{
$expressionResult = $this->processPropertyExpr($builder, $expr);
foreach ($expressionResult->getUniqueJoins($builder) as $join) {
$join->applyJoin($builder);
}
$orderingExpression = $this->processOrderDirection($expressionResult, $direction);
$builder->addOrderBy('%ex', $orderingExpression);
}


/**
* @phpstan-return array{string, list<mixed>}
*/
private function processOrderDirection(DbalExpressionResult $expression, string $direction): array
public function processOrderDirection(DbalExpressionResult $expression, string $direction): array
{
if ($this->platformName === 'mysql') {
if ($direction === ICollection::ASC || $direction === ICollection::ASC_NULLS_FIRST) {
Expand Down

0 comments on commit e1a1204

Please sign in to comment.