diff --git a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php index 3088682e6..bdab365a8 100644 --- a/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php +++ b/packages/database/src/Builder/QueryBuilders/SelectQueryBuilder.php @@ -229,7 +229,7 @@ public function orderBy(string $field, Direction $direction = Direction::ASC): s return $this->orderByRaw($field); } - $this->select->orderBy[] = new OrderByStatement("`{$field}` {$direction->value}"); + $this->select->orderBy[] = new OrderByStatement(field: $field, direction: $direction); return $this; } @@ -241,7 +241,7 @@ public function orderBy(string $field, Direction $direction = Direction::ASC): s */ public function orderByRaw(string $statement): self { - $this->select->orderBy[] = new OrderByStatement($statement); + $this->select->orderBy[] = new RawStatement($statement); return $this; } diff --git a/packages/database/src/QueryStatements/OrderByStatement.php b/packages/database/src/QueryStatements/OrderByStatement.php index a86a6ca87..afa8eeefc 100644 --- a/packages/database/src/QueryStatements/OrderByStatement.php +++ b/packages/database/src/QueryStatements/OrderByStatement.php @@ -3,16 +3,25 @@ namespace Tempest\Database\QueryStatements; use Tempest\Database\Config\DatabaseDialect; +use Tempest\Database\Direction; use Tempest\Database\QueryStatement; +use function Tempest\Support\str; + final readonly class OrderByStatement implements QueryStatement { public function __construct( - private string $orderBy, + private string $field, + private Direction $direction = Direction::ASC, ) {} public function compile(DatabaseDialect $dialect): string { - return $this->orderBy; + $quoted = str($this->field) + ->explode(separator: '.') + ->map($dialect->quoteIdentifier(...)) + ->implode(glue: '.'); + + return "{$quoted} {$this->direction->value}"; } } diff --git a/packages/database/src/QueryStatements/SelectStatement.php b/packages/database/src/QueryStatements/SelectStatement.php index bd18fe9da..879cb23e9 100644 --- a/packages/database/src/QueryStatements/SelectStatement.php +++ b/packages/database/src/QueryStatements/SelectStatement.php @@ -96,7 +96,7 @@ public function compile(DatabaseDialect $dialect): string if ($this->orderBy->isNotEmpty()) { $query[] = 'ORDER BY ' . $this->orderBy - ->map(fn (OrderByStatement $orderBy) => $orderBy->compile($dialect)) + ->map(fn (OrderByStatement|RawStatement $orderBy) => $orderBy->compile($dialect)) ->implode(', '); } diff --git a/packages/database/tests/QueryStatements/SelectStatementTest.php b/packages/database/tests/QueryStatements/SelectStatementTest.php index 988d71c24..8a8945f43 100644 --- a/packages/database/tests/QueryStatements/SelectStatementTest.php +++ b/packages/database/tests/QueryStatements/SelectStatementTest.php @@ -6,6 +6,7 @@ use Tempest\Database\Builder\FieldDefinition; use Tempest\Database\Builder\TableDefinition; use Tempest\Database\Config\DatabaseDialect; +use Tempest\Database\Direction; use Tempest\Database\QueryStatements\GroupByStatement; use Tempest\Database\QueryStatements\HavingStatement; use Tempest\Database\QueryStatements\JoinStatement; @@ -26,7 +27,7 @@ public function test_select(): void fields: arr(['`a`', 'b', 'c', new FieldDefinition($tableDefinition, 'd', 'd_alias')]), join: arr(new JoinStatement('INNER JOIN foo ON bar.id = foo.id')), where: arr(new WhereStatement('`foo` = "bar"')), - orderBy: arr(new OrderByStatement('`foo` DESC')), + orderBy: arr(new OrderByStatement(field: 'foo', direction: Direction::DESC)), groupBy: arr(new GroupByStatement('`foo`')), having: arr(new HavingStatement('`foo` = "bar"')), limit: 10, diff --git a/tests/Integration/Database/QueryStatements/OrderByStatementTest.php b/tests/Integration/Database/QueryStatements/OrderByStatementTest.php new file mode 100644 index 000000000..ffc16bb3f --- /dev/null +++ b/tests/Integration/Database/QueryStatements/OrderByStatementTest.php @@ -0,0 +1,40 @@ +assertSame(expected: $expected, actual: $statement->compile(dialect: $dialect)); + } + + #[Test] + #[TestWith([DatabaseDialect::MYSQL, '`title` DESC'])] + #[TestWith([DatabaseDialect::SQLITE, '`title` DESC'])] + #[TestWith([DatabaseDialect::POSTGRESQL, '"title" DESC'])] + public function bare_column_is_quoted_per_dialect(DatabaseDialect $dialect, string $expected): void + { + $statement = new OrderByStatement(field: 'title', direction: Direction::DESC); + + $this->assertSame(expected: $expected, actual: $statement->compile(dialect: $dialect)); + } +}