diff --git a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php index d12e85816..c56859f09 100644 --- a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php @@ -11,7 +11,9 @@ use Tempest\Database\OnDatabase; use Tempest\Database\Query; use Tempest\Database\QueryStatements\FieldStatement; +use Tempest\Database\QueryStatements\GroupByStatement; use Tempest\Database\QueryStatements\HasWhereStatements; +use Tempest\Database\QueryStatements\HavingStatement; use Tempest\Database\QueryStatements\JoinStatement; use Tempest\Database\QueryStatements\OrderByStatement; use Tempest\Database\QueryStatements\RawStatement; @@ -123,6 +125,24 @@ public function orderBy(string $statement): self return $this; } + /** @return self */ + public function groupBy(string $statement): self + { + $this->select->groupBy[] = new GroupByStatement($statement); + + return $this; + } + + /** @return self */ + public function having(string $statement, mixed ...$bindings): self + { + $this->select->having[] = new HavingStatement($statement); + + $this->bind(...$bindings); + + return $this; + } + /** @return self */ public function limit(int $limit): self { diff --git a/packages/database/src/QueryStatements/SelectStatement.php b/packages/database/src/QueryStatements/SelectStatement.php index 1d6248c09..32c9dee45 100644 --- a/packages/database/src/QueryStatements/SelectStatement.php +++ b/packages/database/src/QueryStatements/SelectStatement.php @@ -54,12 +54,6 @@ public function compile(DatabaseDialect $dialect): string ->implode(PHP_EOL); } - if ($this->orderBy->isNotEmpty()) { - $query[] = 'ORDER BY ' . $this->orderBy - ->map(fn (OrderByStatement $orderBy) => $orderBy->compile($dialect)) - ->implode(', '); - } - if ($this->groupBy->isNotEmpty()) { $query[] = 'GROUP BY ' . $this->groupBy ->map(fn (GroupByStatement $groupBy) => $groupBy->compile($dialect)) @@ -72,6 +66,12 @@ public function compile(DatabaseDialect $dialect): string ->implode(PHP_EOL); } + if ($this->orderBy->isNotEmpty()) { + $query[] = 'ORDER BY ' . $this->orderBy + ->map(fn (OrderByStatement $orderBy) => $orderBy->compile($dialect)) + ->implode(', '); + } + if ($this->limit !== null) { $query[] = 'LIMIT ' . $this->limit; } diff --git a/packages/database/tests/QueryStatements/SelectStatementTest.php b/packages/database/tests/QueryStatements/SelectStatementTest.php index 4ae1e26ec..dcf4f6c22 100644 --- a/packages/database/tests/QueryStatements/SelectStatementTest.php +++ b/packages/database/tests/QueryStatements/SelectStatementTest.php @@ -38,9 +38,9 @@ public function test_select(): void FROM `foo` AS `bar` INNER JOIN foo ON bar.id = foo.id WHERE `foo` = "bar" - ORDER BY `foo` DESC GROUP BY `foo` HAVING `foo` = "bar" + ORDER BY `foo` DESC LIMIT 10 OFFSET 100 SQL; diff --git a/tests/Integration/Database/Builder/SelectQueryBuilderTest.php b/tests/Integration/Database/Builder/SelectQueryBuilderTest.php index 3a885ce1c..0664a9c3f 100644 --- a/tests/Integration/Database/Builder/SelectQueryBuilderTest.php +++ b/tests/Integration/Database/Builder/SelectQueryBuilderTest.php @@ -409,6 +409,38 @@ public function test_eager_loads_combined_with_manual_loads(): void SQL, $query); } + public function test_group_by(): void + { + $sql = query('authors') + ->select() + ->groupBy('name') + ->toSql(); + + $expected = <<assertSameWithoutBackticks($expected, $sql); + } + + public function test_having(): void + { + $sql = query('authors') + ->select() + ->having('name = ?', 'Brent') + ->toSql(); + + $expected = <<assertSameWithoutBackticks($expected, $sql); + } + private function seed(): void { $this->migrate(