Add qualifier support for ORDER BY

This commit is contained in:
Daniel J. Summers 2024-09-29 12:30:26 -04:00
parent d067f8983f
commit 4171040966
2 changed files with 58 additions and 31 deletions

View File

@ -143,6 +143,44 @@ class Query
return "UPDATE $tableName SET data = :data WHERE " . self::whereById(); 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) * 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 public static function orderBy(array $fields): string
{ {
if (empty($fields)) return ""; return empty($fields) ? "" : ' ORDER BY ' . implode(', ', array_map(self::mapToOrderBy(...), $fields));
$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);
} }
} }

View File

@ -253,6 +253,25 @@ class QueryTest extends TestCase
'ORDER BY not constructed correctly'); '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')] #[TestDox('orderBy() succeeds with multiple fields and direction for PostgreSQL')]
public function testOrderBySucceedsWithMultipleFieldsAndDirectionForPostgreSQL(): void public function testOrderBySucceedsWithMultipleFieldsAndDirectionForPostgreSQL(): void
{ {