- WIP on Collation

This commit is contained in:
Dave M. 2024-12-16 18:46:24 +00:00
parent b601939459
commit b5e96eb7e0
8 changed files with 61 additions and 11 deletions

View File

@ -150,6 +150,7 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
public static function escapeIdentifier(string $segment, int $type) : string
{
switch($type) {
default:
case static::IDENTIFIER_DATABASE:
case static::IDENTIFIER_TABLE:
case static::IDENTIFIER_FIELD:

View File

@ -2,9 +2,9 @@
namespace Ulmus\Adapter;
use Collator;
use Ulmus\Common\PdoObject;
use Ulmus\ConnectionAdapter;
use Ulmus\Entity;
use Ulmus\Migration\FieldDefinition;
use Ulmus\{Migration\MigrateInterface, Repository, QueryBuilder};
@ -37,6 +37,7 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
};
$this->exportFunctions($pdo);
$this->exportCollations($pdo);
$this->registerPragma($pdo, $this->pragmaBegin);
}
catch(\PDOException $ex){
@ -196,6 +197,37 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
$pdo->sqliteCreateFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1);
}
public function exportCollations(PdoObject $pdo) : void
{
if ( class_exists('Locale') ) {
# @TODO debug this, case-sensitivity not working properly !!
$collator = new Collator(\Locale::getDefault());
$collator->setStrength(Collator::TERTIARY);
$collator->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
$pdo->sqliteCreateCollation('locale_cmp', fn($e1, $e2) => $collator->compare($e1, $e2));
$pdo->sqliteCreateCollation('locale_cmp_desc', fn($e1, $e2) => $collator->compare($e2, $e1));
$collatorCi = new Collator(\Locale::getDefault());
$collatorCi->setStrength(Collator::SECONDARY);
$collatorCi->setAttribute(Collator::NUMERIC_COLLATION, Collator::ON);
$pdo->sqliteCreateCollation('locale_cmp_ci', fn($e1, $e2) => $collatorCi->compare($e1, $e2));
$pdo->sqliteCreateCollation('locale_cmp_ci_desc', fn($e1, $e2) => $collatorCi->compare($e2, $e1));
}
else {
$comp = fn(string $e1, string $e2) => \iconv('UTF-8', 'ASCII//TRANSLIT', $e1) <=> \iconv('UTF-8', 'ASCII//TRANSLIT', $e2);
$pdo->sqliteCreateCollation('locale_cmp', fn($e1, $e2) => $comp);
$pdo->sqliteCreateCollation('locale_cmp_desc', fn($e2, $e1) => $comp);
$compCi = fn(string $e1, string $e2) => strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e1)) <=> strtoupper(\iconv('UTF-8', 'ASCII//TRANSLIT', $e2));
$pdo->sqliteCreateCollation('locale_cmp_ci', fn($e1, $e2) => $compCi);
$pdo->sqliteCreateCollation('locale_cmp_ci_desc', fn($e2, $e1) => $compCi);
}
}
public static function registerPragma(PdoObject $pdo, array $pragmaList) : void
{
$builder = new QueryBuilder\Sql\SqliteQueryBuilder();

View File

@ -4,5 +4,7 @@ namespace Ulmus\Attribute\Obj;
#[\Attribute(\Attribute::TARGET_CLASS)]
class Collation {
public function __construct(
public string $name = ""
) {}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Ulmus\Attribute\Property;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Collation {
public function __construct(
public string $name = ""
) {}
}

View File

@ -62,7 +62,7 @@ abstract class Sql {
}
};
}
public static function raw(string $sql) : object
{
return static::identifier($sql);
@ -84,8 +84,12 @@ abstract class Sql {
return $value;
}
public static function parameter($value) : string
public static function collate(string $name) : string
{
if ( ! preg_match('/^[a-z0-9$_]+$/i',$name) ) {
throw new \InvalidArgumentException(sprintf("Given identifier '%s' should contains supported characters in function name (a-Z, 0-9, $ and _)", $name));
}
return static::raw(sprintf("COLLATE %s", $name));
}
}

View File

@ -8,6 +8,7 @@ class OrderBy extends Fragment {
public array $orderBy = [];
const SQL_TOKEN = "ORDER BY";
const SQL_COLLATE = "COLLATE";
public function set(array $order) : self
{
@ -16,7 +17,7 @@ class OrderBy extends Fragment {
return $this;
}
public function add(object|string $field, ? string $direction = null) : self
public function add(string|\Stringable $field, ? string $direction = null) : self
{
$this->validateFieldType($field);
@ -30,7 +31,7 @@ class OrderBy extends Fragment {
$list = array_map(function($item) {
list($field, $direction) = $item;
return $field . ( $direction ? " $direction" : "" );
return implode(' ', array_filter([ $field , $direction ?? null ] ));
}, $this->orderBy);
return $this->renderSegments([

View File

@ -260,7 +260,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
return $this;
}
public function orderBy(string|\Stringable $field, ? string $direction = null) : self
public function orderBy(string|\Stringable $field, null|string|\Stringable $direction = null) : self
{
if ( null === $orderBy = $this->getFragment(Query\OrderBy::class) ) {
$orderBy = new Query\OrderBy();
@ -272,7 +272,7 @@ class MysqlQueryBuilder extends SqlQueryBuilder
return $this;
}
public function groupBy(string|object $field) : self
public function groupBy(string|\Stringable $field) : self
{
if ( null === $groupBy = $this->getFragment(Query\GroupBy::class) ) {
$groupBy = new Query\GroupBy();

View File

@ -548,7 +548,7 @@ class Repository implements RepositoryInterface
return $this;
}
public function orderBy(string|object $field, ? string $direction = null) : self
public function orderBy(string|\Stringable $field, null|string|\Stringable $direction = null) : self
{
$this->queryBuilder->orderBy($field, $direction);
@ -558,7 +558,7 @@ class Repository implements RepositoryInterface
# @UNTESTED
public function randomizeOrder() : self
{
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Sql::identifier('CURDATE()+0')));
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Common\Sql::identifier('CURDATE()+0')));
return $this;
}