Skip to content

Commit

Permalink
make atLeast/atMost optional boundaries for CountAggregator
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Mar 14, 2024
1 parent 7d0eebb commit 79cba15
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 32 deletions.
79 changes: 50 additions & 29 deletions src/Collection/Aggregations/CountAggregator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Nextras\Orm\Collection\Functions\Result\DbalTableJoin;
use Nextras\Orm\Exception\InvalidArgumentException;
use function array_filter;
use function array_merge;
use function array_pop;
use function count;

Expand All @@ -18,26 +19,18 @@
*/
class CountAggregator implements Aggregator
{
private int $atLeast;

private int $atMost;

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


/**
* @param literal-string $aggregateKey
*/
public function __construct(
int $atLeast,
int $atMost,
string $aggregateKey = 'count',
private readonly ?int $atLeast,
private readonly ?int $atMost,
private readonly string $aggregateKey = 'count',
)
{
$this->atLeast = $atLeast;
$this->atMost = $atMost;
$this->aggregateKey = $aggregateKey;
if ($this->atLeast === null && $this->atMost === null) {
throw new InvalidArgumentException("At least one of the limitations (\$atLeast or \$atMost) is required.");
}
}


Expand All @@ -50,7 +43,9 @@ public function getAggregateKey(): string
public function aggregateValues(array $values): bool
{
$count = count(array_filter($values));
return $count >= $this->atLeast && $count <= $this->atMost;
if ($this->atLeast !== null && $count >= $this->atLeast) return true;
if ($this->atMost !== null && $count <= $this->atMost) return true;
return false;
}


Expand Down Expand Up @@ -85,19 +80,45 @@ public function aggregateExpression(
groupByColumns: $join->groupByColumns,
);

return new DbalExpressionResult(
expression: 'COUNT(%table.%column) >= %i AND COUNT(%table.%column) <= %i',
args: [
$join->toAlias,
$join->groupByColumns[0],
$this->atLeast,
$join->toAlias,
$join->groupByColumns[0],
$this->atMost,
],
joins: $joins,
groupBy: $expression->groupBy,
isHavingClause: true,
);
if ($this->atLeast !== null && $this->atMost !== null) {
return new DbalExpressionResult(
expression: 'COUNT(%table.%column) >= %i AND COUNT(%table.%column) <= %i',
args: [
$join->toAlias,
$join->groupByColumns[0],
$this->atLeast,
$join->toAlias,
$join->groupByColumns[0],
$this->atMost,
],
joins: $joins,
groupBy: $expression->groupBy,
isHavingClause: true,
);
} elseif ($this->atMost !== null) {
return new DbalExpressionResult(
expression: 'COUNT(%table.%column) <= %i',
args: [
$join->toAlias,
$join->groupByColumns[0],
$this->atMost,
],
joins: $joins,
groupBy: $expression->groupBy,
isHavingClause: true,
);
} else {
return new DbalExpressionResult(
expression: 'COUNT(%table.%column) >= %i',
args: [
$join->toAlias,
$join->groupByColumns[0],
$this->atLeast,
],
joins: $joins,
groupBy: $expression->groupBy,
isHavingClause: true,
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ class CollectionAggregationJoinTest extends DataTestCase
'books->title' => 'Book 1',
'books->translator->id' => null,
]);
$authors->fetchAll();
Assert::same(0, $authors->count());
Assert::same(0, $authors->countStored());

Expand All @@ -74,16 +73,35 @@ class CollectionAggregationJoinTest extends DataTestCase
*/
$authors = $this->orm->authors->findBy([
ICollection::OR,
new CountAggregator(1, 1),
new CountAggregator(atLeast: 1, atMost: 1),
'books->translator->id!=' => null,
'books->price->cents<' => 100,
]);
$authors->fetchAll();
Assert::same(1, $authors->count());
Assert::same(1, $authors->countStored());
}


public function testCountAggregator(): void
{
$authors = $this->orm->authors->findBy([
ICollection::AND,
new CountAggregator(atLeast: 2, atMost: null),
'books->price->cents>=' => 50,
]);
Assert::same(1, $authors->count());
Assert::same(1, $authors->countStored());

$authors = $this->orm->authors->findBy([
ICollection::AND,
new CountAggregator(atLeast: null, atMost: 1),
'books->price->cents>=' => 51,
]);
Assert::same(2, $authors->count());
Assert::same(2, $authors->countStored());
}


public function testHasValueOrEmptyWithFunctions(): void
{
/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT "authors".* FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON (("authors"."id" = "books_count"."author_id") AND "books_count"."price" >= 50) GROUP BY "authors"."id" HAVING ((COUNT("books_count"."id") >= 2));
SELECT COUNT(*) AS count FROM (SELECT "authors"."id" FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON (("authors"."id" = "books_count"."author_id") AND "books_count"."price" >= 50) GROUP BY "authors"."id" HAVING ((COUNT("books_count"."id") >= 2))) temp;
SELECT "authors".* FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON (("authors"."id" = "books_count"."author_id") AND "books_count"."price" >= 51) GROUP BY "authors"."id" HAVING ((COUNT("books_count"."id") <= 1));
SELECT COUNT(*) AS count FROM (SELECT "authors"."id" FROM "public"."authors" AS "authors" LEFT JOIN "books" AS "books_count" ON (("authors"."id" = "books_count"."author_id") AND "books_count"."price" >= 51) GROUP BY "authors"."id" HAVING ((COUNT("books_count"."id") <= 1))) temp;

0 comments on commit 79cba15

Please sign in to comment.