Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Oct 29, 2024
1 parent 126b22f commit dedc0ba
Show file tree
Hide file tree
Showing 36 changed files with 193 additions and 161 deletions.
1 change: 0 additions & 1 deletion src/Collection/Aggregations/Aggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public function aggregateValues(array $values);


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression,
ExpressionContext $context,
): DbalExpressionResult;
Expand Down
3 changes: 1 addition & 2 deletions src/Collection/Aggregations/AnyAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,11 @@ public function aggregateValues(array $values): bool


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression,
ExpressionContext $context,
): DbalExpressionResult
{
if ($context !== ExpressionContext::FilterOr) {
if ($context !== ExpressionContext::FilterOrWithHavingClause) {
// When we are not in OR expression, we may simply filter the joined table by the condition.
// Otherwise, we have to employ a HAVING clause with aggregation function.
return $expression;
Expand Down
1 change: 0 additions & 1 deletion src/Collection/Aggregations/CountAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public function aggregateValues(array $values): bool


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression,
ExpressionContext $context,
): DbalExpressionResult
Expand Down
1 change: 0 additions & 1 deletion src/Collection/Aggregations/NoneAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ public function aggregateValues(array $values): bool


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression,
ExpressionContext $context,
): DbalExpressionResult
Expand Down
1 change: 0 additions & 1 deletion src/Collection/Aggregations/NumericAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public function aggregateValues(array $values): mixed


public function aggregateExpression(
QueryBuilder $queryBuilder,
DbalExpressionResult $expression,
ExpressionContext $context,
): DbalExpressionResult
Expand Down
11 changes: 7 additions & 4 deletions src/Collection/DbalCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ public function orderBy($expression, string $direction = ICollection::ASC): ICol

foreach ($expression as $subExpression => $subDirection) {
$collection->ordering[] = [
$helper->processExpression($collection->queryBuilder, $subExpression, ExpressionContext::FilterAnd, null),
$helper->processExpression($collection->queryBuilder, $subExpression, null),
$subDirection,
];
}
} else {
$collection->ordering[] = [
$helper->processExpression($collection->queryBuilder, $expression, ExpressionContext::ValueExpression, null),
$helper->processExpression($collection->queryBuilder, $expression, null),
$direction,
];
}
Expand Down Expand Up @@ -294,15 +294,18 @@ public function getQueryBuilder(): QueryBuilder
$expression = $helper->processExpression(
builder: $this->queryBuilder,
expression: $args,
context: ExpressionContext::FilterAnd,
aggregator: null,
);
$finalContext = $expression->havingExpression === null
? ExpressionContext::FilterAnd
: ExpressionContext::FilterAndWithHavingClause;
$expression = $expression->collect($finalContext);
$joins = $expression->joins;
$groupBy = $expression->groupBy;
if ($expression->expression !== null) {
$this->queryBuilder->andWhere($expression->expression, ...$expression->args);
}
if ($expression->havingExpression !== null) {
if ($expression->havingExpression !== null && $expression->havingArgs !== []) {
$this->queryBuilder->andHaving($expression->havingExpression, ...$expression->havingArgs);
}
if ($this->mapper->getDatabasePlatform()->getName() === MySqlPlatform::NAME) {
Expand Down
2 changes: 2 additions & 0 deletions src/Collection/Expression/ExpressionContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ enum ExpressionContext
{
case FilterAnd;
case FilterOr;
case FilterAndWithHavingClause;
case FilterOrWithHavingClause;
case ValueExpression;
}
10 changes: 8 additions & 2 deletions src/Collection/Functions/BaseCompareFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ public function processArrayExpression(
}

if ($valueReference->aggregator !== null) {
if ($targetValue === null && $valueReference->value === [] && $this instanceof CompareEqualsFunction) {
return new ArrayExpressionResult(
value: [true],
aggregator: $valueReference->aggregator,
propertyMetadata: null,
);
}
$values = array_map(
function ($value) use ($targetValue): bool {
return $this->evaluateInPhp($value, $targetValue);
Expand All @@ -59,13 +66,12 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
assert(count($args) === 2);

$expression = $helper->processExpression($builder, $args[0], $context, $aggregator);
$expression = $helper->processExpression($builder, $args[0], $aggregator);

if ($expression->valueNormalizer !== null) {
$cb = $expression->valueNormalizer;
Expand Down
5 changes: 2 additions & 3 deletions src/Collection/Functions/BaseNumericAggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -64,7 +63,7 @@ public function processDbalExpression(
throw new InvalidStateException("Cannot apply two aggregations simultaneously.");
}

return $helper->processExpression($builder, $args[0], $context, $this->aggregator)
->applyAggregator($builder, ExpressionContext::ValueExpression);
return $helper->processExpression($builder, $args[0], $this->aggregator)
->applyAggregator(ExpressionContext::ValueExpression,);
}
}
1 change: 0 additions & 1 deletion src/Collection/Functions/CollectionFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult;
}
3 changes: 1 addition & 2 deletions src/Collection/Functions/CompareLikeFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,12 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
assert(count($args) === 2);

$expression = $helper->processExpression($builder, $args[0], $context, $aggregator);
$expression = $helper->processExpression($builder, $args[0], $aggregator);

$likeExpression = $args[1];
assert($likeExpression instanceof LikeExpression);
Expand Down
2 changes: 0 additions & 2 deletions src/Collection/Functions/ConjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -112,7 +111,6 @@ public function processDbalExpression(
helper: $helper,
builder: $builder,
args: $args,
context: $context,
aggregator: $aggregator,
);
}
Expand Down
2 changes: 0 additions & 2 deletions src/Collection/Functions/DisjunctionOperatorFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
Expand All @@ -105,7 +104,6 @@ public function processDbalExpression(
helper: $helper,
builder: $builder,
args: $args,
context: ExpressionContext::FilterOr,
aggregator: $aggregator,
);
}
Expand Down
1 change: 0 additions & 1 deletion src/Collection/Functions/FetchPropertyFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ public function processDbalExpression(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator = null,
): DbalExpressionResult
{
Expand Down
99 changes: 58 additions & 41 deletions src/Collection/Functions/JunctionFunctionTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,59 +61,76 @@ protected function processQueryBuilderExpressionWithModifier(
DbalQueryBuilderHelper $helper,
QueryBuilder $builder,
array $args,
ExpressionContext $context,
?Aggregator $aggregator,
): DbalExpressionResult
{
$processedArgs = [];
$processedHavingArgs = [];
$joins = [];
$groupBy = [];
$columns = [];

[$normalized, $newAggregator] = $this->normalizeFunctions($args);
if ($newAggregator !== null) {
if ($aggregator !== null) throw new InvalidStateException("Cannot apply two aggregations simultaneously.");
$aggregator = $newAggregator;
}

$requiresHaving = false;
$expressions = [];
foreach ($normalized as $collectionFunctionArgs) {
$expression = $helper->processExpression($builder, $collectionFunctionArgs, $context, $aggregator);
$expression = $expression->applyAggregator($builder, $context);
$whereArgs = $expression->getArgsForExpansion();
if ($whereArgs !== []) {
$processedArgs[] = $whereArgs;
}
$havingArgs = $expression->getHavingArgsForExpansion();
if ($havingArgs !== []) {
$processedHavingArgs[] = $havingArgs;
$expressions[] = $expression = $helper->processExpression($builder, $collectionFunctionArgs, $aggregator);
if ($expression->havingExpression !== null) {
$requiresHaving = true;
}
$joins = array_merge($joins, $expression->joins);
$groupBy = array_merge($groupBy, $expression->groupBy);
$columns = array_merge($columns, $expression->columns);
}

if ($context === ExpressionContext::FilterOr && $processedHavingArgs !== []) {
// move all where expressions to HAVING clause
return new DbalExpressionResult(
expression: null,
args: [],
joins: $helper->mergeJoins($dbalModifier, $joins),
groupBy: array_merge($groupBy, $columns),
havingExpression: $dbalModifier,
havingArgs: [array_merge($processedArgs, $processedHavingArgs)],
columns: [],
);
} else {
return new DbalExpressionResult(
expression: $processedArgs === [] ? null : $dbalModifier,
args: $processedArgs === [] ? [] : [$processedArgs],
joins: $helper->mergeJoins($dbalModifier, $joins),
groupBy: $groupBy,
havingExpression: $processedHavingArgs === [] ? null : $dbalModifier,
havingArgs: $processedHavingArgs === [] ? [] : [$processedHavingArgs],
columns: $columns,
);
}
return new DbalExpressionResult(
expression: $dbalModifier,
args: $expressions,
havingExpression: $requiresHaving ? $dbalModifier : null,
collectFun: function (ExpressionContext $context) use ($helper, $dbalModifier) {
/** @var DbalExpressionResult $this */

$processedArgs = [];
$processedHavingArgs = [];
$joins = [];
$groupBy = [];
$columns = [];

if ($dbalModifier === '%or') {
if ($context === ExpressionContext::FilterAnd) {
$finalContext = ExpressionContext::FilterOr;
} elseif ($context === ExpressionContext::FilterAndWithHavingClause) {
$finalContext = ExpressionContext::FilterOrWithHavingClause;
} else {
$finalContext = $context;
}
} else {
$finalContext = $context;
}

foreach ($this->args as $arg) {
assert($arg instanceof DbalExpressionResult);
$expression = $arg->collect($finalContext)->applyAggregator($finalContext);

$whereArgs = $expression->getArgsForExpansion();
if ($whereArgs !== []) {
$processedArgs[] = $whereArgs;
}
$havingArgs = $expression->getHavingArgsForExpansion();
if ($havingArgs !== []) {
$processedHavingArgs[] = $havingArgs;
}
$joins = array_merge($joins, $expression->joins);
$groupBy = array_merge($groupBy, $expression->groupBy);
$columns = array_merge($columns, $expression->columns);
}

return new DbalExpressionResult(
expression: $dbalModifier,
args: [$processedArgs],
havingExpression: $processedHavingArgs === [] ? null : $dbalModifier,
havingArgs: $processedHavingArgs === [] ? [] : [$processedHavingArgs],
joins: $helper->mergeJoins($dbalModifier, $joins),
groupBy: $groupBy,
columns: $columns,
);
},
);
}
}
20 changes: 18 additions & 2 deletions src/Collection/Functions/Result/DbalExpressionResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Nextras\Orm\Collection\Functions\Result;


use mysql_xdevapi\Expression;
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Aggregations\Aggregator;
Expand Down Expand Up @@ -43,6 +44,8 @@ class DbalExpressionResult
* @param PropertyMetadata|null $propertyMetadata Reference to backing property of the expression. If null, the expression is no more a simple property expression.
* @param (callable(mixed): mixed)|null $valueNormalizer Normalizes the value for better PHP comparison, it considers the backing property type.
* @param literal-string|list<literal-string|null>|null $dbalModifier Dbal modifier for particular column. Array if multi-column. Null value means expression is a general expression.
* @param (\Closure(ExpressionContext): DbalExpressionResult)|null $collectFun
* @param-closure-this DbalExpressionResult $collectFun
*/
public function __construct(
public readonly string|null $expression,
Expand All @@ -56,6 +59,8 @@ public function __construct(
public readonly ?PropertyMetadata $propertyMetadata = null,
?callable $valueNormalizer = null,
public readonly string|array|null $dbalModifier = null,
public readonly ?ExpressionContext $context = null,
public readonly \Closure|null $collectFun = null,
)
{
$this->valueNormalizer = $valueNormalizer;
Expand Down Expand Up @@ -161,11 +166,22 @@ public function withHavingArgs(string $havingExpression, array $havingArgs): Dba
}


public function collect(ExpressionContext $context): DbalExpressionResult
{
if ($this->collectFun === null) {
return $this;
} else {
$collectFun = $this->collectFun->bindTo($this);
return $collectFun($context);
}
}


/**
* Applies the aggregator and returns modified expression result.
*/
public function applyAggregator(QueryBuilder $queryBuilder, ExpressionContext $context): DbalExpressionResult
public function applyAggregator(ExpressionContext $context): DbalExpressionResult
{
return $this->aggregator?->aggregateExpression($queryBuilder, $this, $context) ?? $this;
return $this->aggregator?->aggregateExpression($this, $context) ?? $this;
}
}
4 changes: 1 addition & 3 deletions src/Collection/Helpers/DbalQueryBuilderHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Aggregations\Aggregator;
use Nextras\Orm\Collection\Expression\ExpressionContext;
use Nextras\Orm\Collection\Functions\ConjunctionOperatorFunction;
use Nextras\Orm\Collection\Functions\FetchPropertyFunction;
use Nextras\Orm\Collection\Functions\Result\DbalExpressionResult;
Expand Down Expand Up @@ -76,7 +75,6 @@ public function __construct(
public function processExpression(
QueryBuilder $builder,
array|string $expression,
ExpressionContext $context,
?Aggregator $aggregator,
): DbalExpressionResult
{
Expand All @@ -88,7 +86,7 @@ public function processExpression(
}

$collectionFunction = $this->repository->getCollectionFunction($function);
return $collectionFunction->processDbalExpression($this, $builder, $expression, $context, $aggregator);
return $collectionFunction->processDbalExpression($this, $builder, $expression, $aggregator);
}


Expand Down
Loading

0 comments on commit dedc0ba

Please sign in to comment.