Skip to content

Commit

Permalink
Merge pull request #546 from nextras/join_array_dependent
Browse files Browse the repository at this point in the history
ArrayCollection processes conditions as row-dependent in multi-value joins
  • Loading branch information
hrach committed Dec 11, 2021
2 parents ab1c6f1 + 4efc269 commit a5e48e6
Show file tree
Hide file tree
Showing 30 changed files with 613 additions and 282 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Helpers;
namespace Nextras\Orm\Collection\Aggregations;


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalJoinEntry;
use Nextras\Orm\Exception\InvalidStateException;
use function array_merge;
use function array_pop;
use function array_shift;


class DbalAnyAggregator implements IDbalAggregator
/**
* @implements IArrayAggregator<bool>
*/
class AnyAggregator implements IDbalAggregator, IArrayAggregator
{
public function aggregate(
/** @var string */
private $aggregateKey;


public function __construct(string $aggregateKey = 'any')
{
$this->aggregateKey = $aggregateKey;
}


public function getAggregateKey(): string
{
return $this->aggregateKey;
}


public function aggregateValues(array $values): bool
{
foreach ($values as $value) {
if ($value) {
return true;
}
}
return false;
}


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression
): DbalExpressionResult
Expand All @@ -36,12 +67,12 @@ public function aggregate(
);

$primaryKey = $join->conventions->getStoragePrimaryKey()[0];
$queryBuilder->addGroupBy('%table.%column', $join->toAlias, $primaryKey);

return new DbalExpressionResult(
'COUNT(%table.%column) > 0',
[$join->toAlias, $primaryKey],
$joins,
$expression->groupBy,
null,
true,
null,
Expand Down
89 changes: 89 additions & 0 deletions src/Collection/Aggregations/CountAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Aggregations;


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalJoinEntry;
use Nextras\Orm\Exception\InvalidStateException;


/**
* @implements IArrayAggregator<bool>
*/
class CountAggregator implements IDbalAggregator, IArrayAggregator
{
/** @var int */
private $atLeast;

/** @var int */
private $atMost;

/** @var string */
private $aggregateKey;


public function __construct(
int $atLeast,
int $atMost,
string $aggregateKey = 'count'
)
{
$this->atLeast = $atLeast;
$this->atMost = $atMost;
$this->aggregateKey = $aggregateKey;
}


public function getAggregateKey(): string
{
return $this->aggregateKey;
}


public function aggregateValues(array $values): bool
{
$count = count(array_filter($values));
return $count >= $this->atLeast && $count <= $this->atMost;
}


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression
): DbalExpressionResult
{
$joinExpression = $expression->expression;

$joinArgs = $expression->args;
$joins = $expression->joins;
$join = array_pop($joins);
if ($join === null) {
throw new InvalidStateException('Aggregation applied over expression without a relationship');
}

$joins[] = new DbalJoinEntry(
$join->toExpression,
$join->toArgs,
$join->toAlias,
"($join->onExpression) AND $joinExpression",
array_merge($join->onArgs, $joinArgs),
$join->conventions
);

$primaryKey = $join->conventions->getStoragePrimaryKey()[0];
$groupBy = $expression->groupBy;

return new DbalExpressionResult(
'COUNT(%table.%column) >= %i AND COUNT(%table.%column) <= %i',
[$join->toAlias, $primaryKey, $this->atLeast, $join->toAlias, $primaryKey, $this->atMost],
$joins,
$groupBy,
null,
true,
null,
null
);
}
}
9 changes: 9 additions & 0 deletions src/Collection/Aggregations/IAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Aggregations;


interface IAggregator
{
public function getAggregateKey(): string;
}
16 changes: 16 additions & 0 deletions src/Collection/Aggregations/IArrayAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Aggregations;


/**
* @template T
*/
interface IArrayAggregator extends IAggregator
{
/**
* @param array<T> $values
* @return T
*/
function aggregateValues(array $values);
}
16 changes: 16 additions & 0 deletions src/Collection/Aggregations/IDbalAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Aggregations;


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;


interface IDbalAggregator extends IAggregator
{
public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expressionResult
): DbalExpressionResult;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Helpers;
namespace Nextras\Orm\Collection\Aggregations;


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalJoinEntry;
use Nextras\Orm\Exception\InvalidStateException;
use function array_merge;
use function array_pop;
use function array_shift;


class DbalNoneAggregator implements IDbalAggregator
/**
* @implements IArrayAggregator<bool>
*/
class NoneAggregator implements IDbalAggregator, IArrayAggregator
{
public function aggregate(
/** @var string */
private $aggregateKey;


public function __construct(string $aggregateKey = 'none')
{
$this->aggregateKey = $aggregateKey;
}


public function getAggregateKey(): string
{
return $this->aggregateKey;
}


public function aggregateValues(array $values): bool
{
foreach ($values as $value) {
if ($value) {
return false;
}
}
return true;
}


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression
): DbalExpressionResult
Expand All @@ -36,12 +67,12 @@ public function aggregate(
);

$primaryKey = $join->conventions->getStoragePrimaryKey()[0];
$queryBuilder->addGroupBy('%table.%column', $join->toAlias, $primaryKey);

return new DbalExpressionResult(
'COUNT(%table.%column) = 0',
[$join->toAlias, $primaryKey],
$joins,
$expression->groupBy,
null,
true,
null,
Expand Down
7 changes: 5 additions & 2 deletions src/Collection/ArrayCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Iterator;
use Nette\Utils\Arrays;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\FetchPairsHelper;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -60,7 +61,7 @@ class ArrayCollection implements ICollection, MemoryCollection

/**
* @var Closure[]
* @phpstan-var list<Closure(E): mixed>
* @phpstan-var array<Closure(E): ArrayPropertyValueReference>
*/
protected $collectionFilter = [];

Expand Down Expand Up @@ -294,7 +295,9 @@ protected function processData(): void
$data = $this->data;

foreach ($this->collectionFilter as $filter) {
$data = array_filter($data, $filter);
$data = array_filter($data, function ($value) use ($filter) {
return $filter($value)->value;
});
}

if (count($this->collectionSorter) > 0) {
Expand Down
11 changes: 11 additions & 0 deletions src/Collection/DbalCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Nextras\Orm\Mapper\IRelationshipMapper;
use function count;
use function is_array;
use function str_repeat;


/**
Expand Down Expand Up @@ -313,6 +314,7 @@ public function __clone()
public function getQueryBuilder(): QueryBuilder
{
$joins = [];
$groupBy = [];
$helper = $this->getHelper();
$args = $this->filtering;

Expand All @@ -324,6 +326,7 @@ public function getQueryBuilder(): QueryBuilder
null
);
$joins = $expression->joins;
$groupBy = $expression->groupBy;
if ($expression->isHavingClause) {
$this->queryBuilder->andHaving($expression->expression, ...$expression->args);
} else {
Expand All @@ -334,6 +337,7 @@ public function getQueryBuilder(): QueryBuilder

foreach ($this->ordering as [$expression, $direction]) {
$joins = array_merge($joins, $expression->joins);
$groupBy = array_merge($groupBy, $expression->groupBy);
$orderingExpression = $helper->processOrderDirection($expression, $direction);
$this->queryBuilder->addOrderBy('%ex', $orderingExpression);
}
Expand All @@ -344,6 +348,13 @@ public function getQueryBuilder(): QueryBuilder
$join->applyJoin($this->queryBuilder);
}

if (count($groupBy) > 0) {
$this->queryBuilder->groupBy(
'%ex' . str_repeat(', %ex', count($groupBy) - 1),
...$groupBy
);
}

return $this->queryBuilder;
}

Expand Down
48 changes: 0 additions & 48 deletions src/Collection/Functions/AnyAggregateFunction.php

This file was deleted.

0 comments on commit a5e48e6

Please sign in to comment.