From 417104096654ed282d0a27a830db373eb685a62f Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sun, 29 Sep 2024 12:30:26 -0400 Subject: [PATCH] Add qualifier support for ORDER BY --- src/Query.php | 70 ++++++++++++++++++++++------------------ tests/unit/QueryTest.php | 19 +++++++++++ 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/Query.php b/src/Query.php index ebe6211..66c39b6 100644 --- a/src/Query.php +++ b/src/Query.php @@ -143,6 +143,44 @@ class Query return "UPDATE $tableName SET data = :data WHERE " . self::whereById(); } + /** + * Transform a field to an ORDER BY clause segment + * + * @param Field $field The field by which ordering should be implemented + * @return string The ORDER BY fragment for the given field + * @throws Exception If the database mode has not been set + */ + private static function mapToOrderBy(Field $field): string + { + $mode = Configuration::mode('render ORDER BY clause'); + + if (str_contains($field->fieldName, ' ')) { + $parts = explode(' ', $field->fieldName); + $field->fieldName = array_shift($parts); + $direction = ' ' . implode(' ', $parts); + } else { + $direction = ''; + } + + if (str_starts_with($field->fieldName, 'n:')) { + $field->fieldName = substr($field->fieldName, 2); + $value = match ($mode) { + Mode::PgSQL => '(' . $field->path() . ')::numeric', + Mode::SQLite => $field->path() + }; + } elseif (str_starts_with($field->fieldName, 'i:')) { + $field->fieldName = substr($field->fieldName, 2); + $value = match ($mode) { + Mode::PgSQL => 'LOWER(' . $field->path() . ')', + Mode::SQLite => $field->path() . ' COLLATE NOCASE' + }; + } else { + $value = $field->path(); + } + + return (empty($field->qualifier) ? '' : "$field->qualifier.") . $value . $direction; + } + /** * Create an `ORDER BY` clause ('n:' treats field as number, 'i:' does case-insensitive ordering) * @@ -152,36 +190,6 @@ class Query */ public static function orderBy(array $fields): string { - if (empty($fields)) return ""; - - $mode = Configuration::mode('render ORDER BY clause'); - $sqlFields = array_map(function (Field $it) use ($mode) { - if (str_contains($it->fieldName, ' ')) { - $parts = explode(' ', $it->fieldName); - $it->fieldName = array_shift($parts); - $direction = ' ' . implode(' ', $parts); - } else { - $direction = ''; - } - - if (str_starts_with($it->fieldName, 'n:')) { - $it->fieldName = substr($it->fieldName, 2); - $value = match ($mode) { - Mode::PgSQL => '(' . $it->path() . ')::numeric', - Mode::SQLite => $it->path() - }; - } elseif (str_starts_with($it->fieldName, 'i:')) { - $it->fieldName = substr($it->fieldName, 2); - $value = match ($mode) { - Mode::PgSQL => 'LOWER(' . $it->path() . ')', - Mode::SQLite => $it->path() . ' COLLATE NOCASE' - }; - } else { - $value = $it->path(); - } - - return $value . $direction; - }, $fields); - return ' ORDER BY ' . implode(', ', $sqlFields); + return empty($fields) ? "" : ' ORDER BY ' . implode(', ', array_map(self::mapToOrderBy(...), $fields)); } } diff --git a/tests/unit/QueryTest.php b/tests/unit/QueryTest.php index 66eb63f..75606a4 100644 --- a/tests/unit/QueryTest.php +++ b/tests/unit/QueryTest.php @@ -253,6 +253,25 @@ class QueryTest extends TestCase 'ORDER BY not constructed correctly'); } + #[TestDox('orderBy() succeeds with one qualified field for PostgreSQL')] + public function testOrderBySucceedsWithOneQualifiedFieldForPostgreSQL(): void + { + Configuration::overrideMode(Mode::PgSQL); + $field = Field::named('TestField'); + $field->qualifier = 'qual'; + $this->assertEquals(" ORDER BY qual.data->>'TestField'", Query::orderBy([$field]), + 'ORDER BY not constructed correctly'); + } + + #[TestDox('orderBy() succeeds with one qualified field for SQLite')] + public function testOrderBySucceedsWithOneQualifiedFieldForSQLite(): void + { + $field = Field::named('TestField'); + $field->qualifier = 'qual'; + $this->assertEquals(" ORDER BY qual.data->>'TestField'", Query::orderBy([$field]), + 'ORDER BY not constructed correctly'); + } + #[TestDox('orderBy() succeeds with multiple fields and direction for PostgreSQL')] public function testOrderBySucceedsWithMultipleFieldsAndDirectionForPostgreSQL(): void {