Skip to content

Commit

Permalink
any/none filtering: implement Array support
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Aug 4, 2021
1 parent 27bfe90 commit 11aeb3d
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/Collection/ArrayCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public function getByIdChecked($id): IEntity
public function findBy(array $conds): ICollection
{
$collection = clone $this;
$collection->collectionFilter[] = $this->getHelper()->createFilter($conds);
$collection->collectionFilter[] = $this->getHelper()->createFilter($conds, null);
return $collection;
}

Expand Down
21 changes: 20 additions & 1 deletion src/Collection/Functions/AnyAggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\ArrayAnyAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\DbalAnyAggregator;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Exception\InvalidStateException;


class AnyAggregateFunction implements IQueryBuilderFunction
class AnyAggregateFunction implements IArrayFunction, IQueryBuilderFunction
{
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
if ($aggregator !== null) {
throw new InvalidStateException("Cannot apply two aggregations simultaneously.");
}

return $helper->getValue($entity, $args[0], new ArrayAnyAggregator())->value;
}


public function processQueryBuilderExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
Expand Down
10 changes: 8 additions & 2 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\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Exception\InvalidArgumentException;
Expand Down Expand Up @@ -36,11 +37,16 @@ protected function __construct(string $sqlFunction)
abstract protected function calculateAggregation(array $values);


public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
assert(count($args) === 1 && is_string($args[0]));

$valueReference = $helper->getValue($entity, $args[0]);
$valueReference = $helper->getValue($entity, $args[0], $aggregator);
if (!$valueReference->isMultiValue) {
throw new InvalidArgumentException('Aggregation has to be called over has many relationship.');
}
Expand Down
25 changes: 17 additions & 8 deletions src/Collection/Functions/BaseCompareFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\ArrayAnyAggregator;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;
use function assert;
Expand All @@ -15,24 +17,31 @@

abstract class BaseCompareFunction implements IArrayFunction, IQueryBuilderFunction
{
public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
assert(count($args) === 2);

$valueReference = $helper->getValue($entity, $args[0]);
$valueReference = $helper->getValue($entity, $args[0], $aggregator);
if ($valueReference->propertyMetadata !== null) {
$targetValue = $helper->normalizeValue($args[1], $valueReference->propertyMetadata, true);
} else {
$targetValue = $args[1];
}

if ($valueReference->isMultiValue) {
foreach ($valueReference->value as $subValue) {
if ($this->evaluateInPhp($subValue, $targetValue)) {
return true;
}
}
return false;
$values = array_map(
function ($value) use ($targetValue) {
return $this->evaluateInPhp($value, $targetValue);
},
$valueReference->value
);
$aggregator = $valueReference->aggregator ?? new ArrayAnyAggregator();
return $aggregator->aggregate($values);
} else {
return $this->evaluateInPhp($valueReference->value, $targetValue);
}
Expand Down
10 changes: 8 additions & 2 deletions src/Collection/Functions/ConjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;

Expand All @@ -24,10 +25,15 @@ public function __construct(ConditionParser $conditionParserHelper)
}


public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
foreach ($this->normalizeFunctions($args) as $arg) {
$callback = $helper->createFilter($arg);
$callback = $helper->createFilter($arg, $aggregator);
if ($callback($entity) == false) { // intentionally ==
return false;
}
Expand Down
10 changes: 8 additions & 2 deletions src/Collection/Functions/DisjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Nextras\Orm\Collection\Helpers\ConditionParser;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;

Expand All @@ -24,10 +25,15 @@ public function __construct(ConditionParser $conditionParserHelper)
}


public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args)
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
foreach ($this->normalizeFunctions($args) as $arg) {
$callback = $helper->createFilter($arg);
$callback = $helper->createFilter($arg, $aggregator);
if ($callback($entity) == true) { // intentionally ==
return true;
}
Expand Down
8 changes: 7 additions & 1 deletion src/Collection/Functions/IArrayFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


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


Expand All @@ -20,5 +21,10 @@ interface IArrayFunction
* @phpstan-param array<int|string, mixed> $args
* @return mixed
*/
public function processArrayExpression(ArrayCollectionHelper $helper, IEntity $entity, array $args);
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
);
}
21 changes: 20 additions & 1 deletion src/Collection/Functions/NoneAggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@


use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Helpers\ArrayCollectionHelper;
use Nextras\Orm\Collection\Helpers\ArrayNoneAggregator;
use Nextras\Orm\Collection\Helpers\DbalExpressionResult;
use Nextras\Orm\Collection\Helpers\DbalNoneAggregator;
use Nextras\Orm\Collection\Helpers\DbalQueryBuilderHelper;
use Nextras\Orm\Collection\Helpers\IArrayAggregator;
use Nextras\Orm\Collection\Helpers\IDbalAggregator;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Exception\InvalidStateException;


class NoneAggregateFunction implements IQueryBuilderFunction
class NoneAggregateFunction implements IArrayFunction, IQueryBuilderFunction
{
public function processArrayExpression(
ArrayCollectionHelper $helper,
IEntity $entity,
array $args,
?IArrayAggregator $aggregator = null
)
{
if ($aggregator !== null) {
throw new InvalidStateException("Cannot apply two aggregations simultaneously.");
}

return $helper->getValue($entity, $args[0], new ArrayNoneAggregator())->value;
}


public function processQueryBuilderExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
Expand Down
20 changes: 20 additions & 0 deletions src/Collection/Helpers/ArrayAnyAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Helpers;


/**
* @implements IArrayAggregator<bool>
*/
class ArrayAnyAggregator implements IArrayAggregator
{
public function aggregate(array $values): bool
{
foreach ($values as $value) {
if ($value) {
return true;
}
}
return false;
}
}
27 changes: 15 additions & 12 deletions src/Collection/Helpers/ArrayCollectionHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ public function __construct(IRepository $repository)
* @phpstan-param array<string, mixed>|list<mixed> $expr
* @phpstan-return Closure(IEntity): mixed
*/
public function createFilter(array $expr): Closure
public function createFilter(array $expr, ?IArrayAggregator $aggregator): Closure
{
$function = isset($expr[0]) ? array_shift($expr) : ICollection::AND;
$customFunction = $this->repository->getCollectionFunction($function);
if (!$customFunction instanceof IArrayFunction) {
throw new InvalidStateException("Collection function $function has to implement " . IArrayFunction::class . ' interface.');
}

return function (IEntity $entity) use ($customFunction, $expr) {
return $customFunction->processArrayExpression($this, $entity, $expr);
return function (IEntity $entity) use ($customFunction, $expr, $aggregator) {
return $customFunction->processArrayExpression($this, $entity, $expr, $aggregator);
};
}

Expand Down Expand Up @@ -93,8 +93,8 @@ public function createSorter(array $expressions): Closure
$_b = $expression[0]->processArrayExpression($this, $b, $expression[2]);
} else {
assert($expression[2] instanceof EntityMetadata);
$_a = $this->getValueByTokens($a, $expression[0], $expression[2])->value;
$_b = $this->getValueByTokens($b, $expression[0], $expression[2])->value;
$_a = $this->getValueByTokens($a, $expression[0], $expression[2], null)->value;
$_b = $this->getValueByTokens($b, $expression[0], $expression[2], null)->value;
}

$ordering = $expression[1];
Expand Down Expand Up @@ -124,21 +124,21 @@ public function createSorter(array $expressions): Closure
* @param string|array $expr
* @phpstan-param string|array<string, mixed>|list<mixed> $expr
*/
public function getValue(IEntity $entity, $expr): ArrayPropertyValueReference
public function getValue(IEntity $entity, $expr, ?IArrayAggregator $aggregator): ArrayPropertyValueReference
{
if (is_array($expr)) {
$function = array_shift($expr);
$function = isset($expr[0]) ? array_shift($expr) : ICollection::AND;
$collectionFunction = $this->repository->getCollectionFunction($function);
if (!$collectionFunction instanceof IArrayFunction) {
throw new InvalidStateException("Collection function $function has to implement " . IArrayFunction::class . ' interface.');
}
$value = $collectionFunction->processArrayExpression($this, $entity, $expr);
return new ArrayPropertyValueReference($value, false, null);
$value = $collectionFunction->processArrayExpression($this, $entity, $expr, $aggregator);
return new ArrayPropertyValueReference($value, false, null, null);
}

[$tokens, $sourceEntityClassName] = $this->repository->getConditionParser()->parsePropertyExpr($expr);
$sourceEntityMeta = $this->repository->getEntityMetadata($sourceEntityClassName);
return $this->getValueByTokens($entity, $tokens, $sourceEntityMeta);
return $this->getValueByTokens($entity, $tokens, $sourceEntityMeta, $aggregator);
}


Expand Down Expand Up @@ -197,7 +197,8 @@ public function normalizeValue($value, PropertyMetadata $propertyMetadata, bool
private function getValueByTokens(
IEntity $entity,
array $expressionTokens,
EntityMetadata $sourceEntityMeta
EntityMetadata $sourceEntityMeta,
?IArrayAggregator $aggregator
): ArrayPropertyValueReference
{
if (!$entity instanceof $sourceEntityMeta->className) {
Expand All @@ -209,6 +210,7 @@ public function __toString()
}
},
false,
null,
null
);
}
Expand Down Expand Up @@ -260,7 +262,8 @@ public function __toString()
return new ArrayPropertyValueReference(
$isMultiValue ? $values : $values[0],
$isMultiValue,
$propertyMeta
$propertyMeta,
$isMultiValue ? ($aggregator ?? new ArrayAnyAggregator()) : null
);
}
}
20 changes: 20 additions & 0 deletions src/Collection/Helpers/ArrayNoneAggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);

namespace Nextras\Orm\Collection\Helpers;


/**
* @implements IArrayAggregator<bool>
*/
class ArrayNoneAggregator implements IArrayAggregator
{
public function aggregate(array $values): bool
{
foreach ($values as $value) {
if ($value) {
return false;
}
}
return true;
}
}
Loading

0 comments on commit 11aeb3d

Please sign in to comment.