Skip to content

Commit

Permalink
collection: rework Array aggregation to process values in Con|Disjunc…
Browse files Browse the repository at this point in the history
…tion operators
  • Loading branch information
hrach committed Nov 26, 2021
1 parent 1682144 commit a40462e
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 49 deletions.
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: 8 additions & 3 deletions src/Collection/Functions/BaseAggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Nextras\Orm\Collection\Aggregations\IArrayAggregator;
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;
Expand Down Expand Up @@ -42,17 +43,21 @@ public function processArrayExpression(
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
): ArrayPropertyValueReference
{
assert(count($args) === 1 && is_string($args[0]));

$valueReference = $helper->getValue($entity, $args[0], $aggregator);
if (!$valueReference->isMultiValue) {
if ($valueReference->aggregator === null) {
throw new InvalidArgumentException('Aggregation has to be called over has many relationship.');
}
assert(is_array($valueReference->value));

return $this->calculateAggregation($valueReference->value);
return new ArrayPropertyValueReference(
$this->calculateAggregation($valueReference->value),
null,
null
);
}


Expand Down
19 changes: 13 additions & 6 deletions src/Collection/Functions/BaseCompareFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Aggregations\AnyAggregator;
use Nextras\Orm\Collection\Aggregations\IArrayAggregator;
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;
Expand All @@ -22,7 +22,7 @@ public function processArrayExpression(
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
): ArrayPropertyValueReference
{
assert(count($args) === 2);

Expand All @@ -33,17 +33,24 @@ public function processArrayExpression(
$targetValue = $args[1];
}

if ($valueReference->isMultiValue) {
if ($valueReference->aggregator !== null) {
$values = array_map(
function ($value) use ($targetValue): bool {
return $this->evaluateInPhp($value, $targetValue);
},
$valueReference->value
);
$aggregator = $valueReference->aggregator ?? new AnyAggregator();
return $aggregator->aggregateValues($values);
return new ArrayPropertyValueReference(
$values,
$valueReference->aggregator,
null
);
} else {
return $this->evaluateInPhp($valueReference->value, $targetValue);
return new ArrayPropertyValueReference(
$this->evaluateInPhp($valueReference->value, $targetValue),
null,
null
);
}
}

Expand Down
28 changes: 19 additions & 9 deletions src/Collection/Functions/CompareLikeFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Expression\LikeExpression;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Entity\IEntity;
Expand All @@ -24,7 +25,7 @@ public function processArrayExpression(
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
): ArrayPropertyValueReference
{
assert(count($args) === 2);

Expand All @@ -40,15 +41,24 @@ public function processArrayExpression(
$targetValue = $likeExpression->getInput();
}

if ($valueReference->isMultiValue) {
foreach ($valueReference->value as $subValue) {
if ($this->evaluateInPhp($mode, $subValue, $targetValue)) {
return true;
}
}
return false;
if ($valueReference->aggregator !== null) {
$values = array_map(
function ($value) use ($mode, $targetValue): bool {
return $this->evaluateInPhp($mode, $value, $targetValue);
},
$valueReference->value
);
return new ArrayPropertyValueReference(
$values,
$valueReference->aggregator,
null
);
} else {
return $this->evaluateInPhp($mode, $valueReference->value, $targetValue);
return new ArrayPropertyValueReference(
$this->evaluateInPhp($mode, $valueReference->value, $targetValue),
null,
null
);
}
}

Expand Down
20 changes: 16 additions & 4 deletions src/Collection/Functions/ConjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Nextras\Orm\Collection\Aggregations\IArrayAggregator;
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
Expand Down Expand Up @@ -34,7 +35,7 @@ public function processArrayExpression(
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
): ArrayPropertyValueReference
{
[$normalized, $newAggregator] = $this->normalizeFunctions($args);
if ($newAggregator !== null) {
Expand All @@ -44,11 +45,22 @@ public function processArrayExpression(

foreach ($normalized as $arg) {
$callback = $helper->createFilter($arg, $aggregator);
if ($callback($entity) == false) { // intentionally ==
return false;
$valueReference = $callback($entity);
$valueReference = $valueReference->applyAggregator();
if ($valueReference->value == false) { // intentionally ==
return new ArrayPropertyValueReference(
/* $result = */ false,
null,
null
);
}
}
return true;

return new ArrayPropertyValueReference(
/* $result = */ true,
null,
null
);
}


Expand Down
20 changes: 16 additions & 4 deletions src/Collection/Functions/DisjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Nextras\Orm\Collection\Aggregations\IArrayAggregator;
use Nextras\Orm\Collection\Aggregations\IDbalAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
Expand Down Expand Up @@ -34,7 +35,7 @@ public function processArrayExpression(
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
): ArrayPropertyValueReference
{
[$normalized, $newAggregator] = $this->normalizeFunctions($args);
if ($newAggregator !== null) {
Expand All @@ -44,11 +45,22 @@ public function processArrayExpression(

foreach ($normalized as $arg) {
$callback = $helper->createFilter($arg, $aggregator);
if ($callback($entity) == true) { // intentionally ==
return true;
$valueReference = $callback($entity);
$valueReference = $valueReference->applyAggregator();
if ($valueReference->value == true) { // intentionally ==
return new ArrayPropertyValueReference(
/* $result = */ true,
null,
null
);
}
}
return false;

return new ArrayPropertyValueReference(
/* $result = */ false,
null,
null
);
}


Expand Down
4 changes: 2 additions & 2 deletions src/Collection/Functions/IArrayFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use Nextras\Orm\Collection\Aggregations\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayPropertyValueReference;
use Nextras\Orm\Entity\IEntity;


Expand All @@ -20,12 +21,11 @@ interface IArrayFunction
* Usually returns a boolean for filtering evaluation.
* @phpstan-param array<int|string, mixed> $args
* @phpstan-param IArrayAggregator<mixed>|null $aggregator
* @return mixed
*/
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
);
): ArrayPropertyValueReference;
}
15 changes: 6 additions & 9 deletions src/Collection/Helpers/ArrayCollectionHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function __construct(IRepository $repository)
/**
* @phpstan-param array<string, mixed>|list<mixed> $expr
* @phpstan-param IArrayAggregator<mixed>|null $aggregator
* @phpstan-return Closure(IEntity): mixed
* @phpstan-return Closure(IEntity): ArrayPropertyValueReference
*/
public function createFilter(array $expr, ?IArrayAggregator $aggregator): Closure
{
Expand Down Expand Up @@ -92,8 +92,8 @@ public function createSorter(array $expressions): Closure
foreach ($parsedExpressions as $expression) {
if ($expression[0] instanceof IArrayFunction) {
assert(is_array($expression[2]));
$_a = $expression[0]->processArrayExpression($this, $a, $expression[2]);
$_b = $expression[0]->processArrayExpression($this, $b, $expression[2]);
$_a = $expression[0]->processArrayExpression($this, $a, $expression[2])->value;
$_b = $expression[0]->processArrayExpression($this, $b, $expression[2])->value;
} else {
assert($expression[2] instanceof EntityMetadata);
$_a = $this->getValueByTokens($a, $expression[0], $expression[2], null)->value;
Expand Down Expand Up @@ -136,8 +136,7 @@ public function getValue(IEntity $entity, $expr, ?IArrayAggregator $aggregator):
if (!$collectionFunction instanceof IArrayFunction) {
throw new InvalidStateException("Collection function $function has to implement " . IArrayFunction::class . ' interface.');
}
$value = $collectionFunction->processArrayExpression($this, $entity, $expr, $aggregator);
return new ArrayPropertyValueReference($value, false, null, null);
return $collectionFunction->processArrayExpression($this, $entity, $expr, $aggregator);
}

[$tokens, $sourceEntityClassName] = $this->repository->getConditionParser()->parsePropertyExpr($expr);
Expand Down Expand Up @@ -214,7 +213,6 @@ public function __toString()
return "undefined";
}
},
false,
null,
null
);
Expand Down Expand Up @@ -266,9 +264,8 @@ public function __toString()

return new ArrayPropertyValueReference(
$isMultiValue ? $values : $values[0],
$isMultiValue,
$propertyMeta,
$isMultiValue ? ($aggregator ?? new AnyAggregator()) : null
$isMultiValue ? ($aggregator ?? new AnyAggregator()) : null,
$propertyMeta
);
}
}
26 changes: 16 additions & 10 deletions src/Collection/Helpers/ArrayPropertyValueReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,6 @@ class ArrayPropertyValueReference
*/
public $value;

/**
* Bool if expression evaluated to multiple values (i.e. fetched has-many relationship values).
* @var bool
*/
public $isMultiValue;

/**
* Reference to backing property of the expression.
* If null, the expression is no more a simple property expression.
Expand All @@ -44,14 +38,26 @@ class ArrayPropertyValueReference
*/
public function __construct(
$value,
bool $isMultiValue,
?PropertyMetadata $propertyMetadata,
?IArrayAggregator $aggregator
?IArrayAggregator $aggregator,
?PropertyMetadata $propertyMetadata
)
{
$this->value = $value;
$this->isMultiValue = $isMultiValue;
$this->propertyMetadata = $propertyMetadata;
$this->aggregator = $aggregator;
}


public function applyAggregator(): ArrayPropertyValueReference
{
if ($this->aggregator === null) {
return $this;
}

return new ArrayPropertyValueReference(
$this->aggregator->aggregateValues($this->value),
null,
null
);
}
}

0 comments on commit a40462e

Please sign in to comment.