- A lot of work done on bug fixing. Added a new SQLite adapter
This commit is contained in:
parent
667df92e52
commit
c14644cdda
|
@ -3,6 +3,7 @@
|
|||
namespace Ulmus\Adapter;
|
||||
|
||||
use Ulmus\Common\PdoObject;
|
||||
use Ulmus\Migration\FieldDefinition;
|
||||
|
||||
interface AdapterInterface {
|
||||
public const IDENTIFIER_FIELD = 1;
|
||||
|
@ -10,10 +11,16 @@ interface AdapterInterface {
|
|||
public const IDENTIFIER_DATABASE = 3;
|
||||
public const IDENTIFIER_SCHEMA = 4;
|
||||
public const IDENTIFIER_VALUE = 5;
|
||||
|
||||
|
||||
public function connect() : object /* | PdoObject|mixed */;
|
||||
public function buildDataSourceName() : string;
|
||||
public function setup(array $configuration) : void;
|
||||
public function escapeIdentifier(string $segment, int $type) : string;
|
||||
public function defaultEngine() : ? string;
|
||||
public function databaseName() : string;
|
||||
public function mapFieldType(FieldDefinition $field) : string;
|
||||
public function schemaTable(string $databaseName, string $tableName) /*: object|EntityCollection */;
|
||||
public function repositoryClass() : string;
|
||||
public function queryBuilderClass() : string;
|
||||
public function tableSyntax() : array;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Adapter;
|
||||
|
||||
use Ulmus\{Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder};
|
||||
|
||||
trait DefaultAdapterTrait
|
||||
{
|
||||
public function repositoryClass() : string
|
||||
{
|
||||
return Repository::class;
|
||||
}
|
||||
|
||||
public function queryBuilderClass() : string
|
||||
{
|
||||
return QueryBuilder::class;
|
||||
}
|
||||
|
||||
public function tableSyntax() : array
|
||||
{
|
||||
return [
|
||||
'ai' => "AUTO_INCREMENT",
|
||||
'pk' => "PRIMARY KEY",
|
||||
'unsigned' => "UNSIGNED",
|
||||
];
|
||||
}
|
||||
|
||||
public function databaseName() : string
|
||||
{
|
||||
return $this->database;
|
||||
}
|
||||
|
||||
public function schemaTable(string $databaseName, string $tableName) /* : ? object */
|
||||
{
|
||||
return Table::repository()->where(Table::field('schema'), $databaseName)->loadOneFromField(Table::field('name'), $tableName);
|
||||
}
|
||||
|
||||
public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string
|
||||
{
|
||||
$type = $field->type;
|
||||
|
||||
$length = $field->length;
|
||||
|
||||
if ( is_a($type, Entity\Field\Date::class, true) ) {
|
||||
$type = "DATE";
|
||||
}
|
||||
elseif ( is_a($type, Entity\Field\Time::class, true) ) {
|
||||
$type = "TIME";
|
||||
}
|
||||
elseif ( is_a($type, \DateTime::class, true) ) {
|
||||
$type = "DATETIME";
|
||||
}
|
||||
|
||||
switch($type) {
|
||||
case "bool":
|
||||
$type = "TINYINT";
|
||||
$length = 1;
|
||||
break;
|
||||
|
||||
case "array":
|
||||
case "string":
|
||||
if ($length && $length <= 255) {
|
||||
$type = "VARCHAR";
|
||||
break;
|
||||
}
|
||||
elseif (! $length || ( $length <= 65535 ) ) {
|
||||
$type = "TEXT";
|
||||
}
|
||||
elseif ( $length <= 16777215 ) {
|
||||
$type = "MEDIUMTEXT";
|
||||
}
|
||||
elseif ($length <= 4294967295) {
|
||||
$type = "LONGTEXT";
|
||||
}
|
||||
else {
|
||||
throw new \Exception("A column with size bigger than 4GB cannot be created.");
|
||||
}
|
||||
|
||||
# Length is unnecessary on TEXT fields
|
||||
unset($length);
|
||||
|
||||
break;
|
||||
|
||||
case "float":
|
||||
$type = "DOUBLE";
|
||||
break;
|
||||
|
||||
default:
|
||||
$type = strtoupper($type);
|
||||
break;
|
||||
}
|
||||
|
||||
return $typeOnly ? $type : $type . ( $length ? "($length" . ( $precision ? ",$precision" : "" ) . ")" : "" );
|
||||
}
|
||||
}
|
|
@ -5,8 +5,13 @@ namespace Ulmus\Adapter;
|
|||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Migration\FieldDefinition;
|
||||
use Ulmus\{Entity\InformationSchema\Table, Repository, QueryBuilder};
|
||||
|
||||
class MsSQL implements AdapterInterface {
|
||||
use DefaultAdapterTrait;
|
||||
|
||||
const DSN_PREFIX = "sqlsrv";
|
||||
|
||||
public int $port;
|
||||
|
||||
|
@ -129,7 +134,7 @@ class MsSQL implements AdapterInterface {
|
|||
$parts[] = "WSID={$this->wsid}";
|
||||
}
|
||||
|
||||
return "sqlsrv:" . implode(';', $parts);
|
||||
return static::DSN_PREFIX . ":" . implode(';', $parts);
|
||||
}
|
||||
|
||||
public function setup(array $configuration) : void
|
||||
|
|
|
@ -2,11 +2,18 @@
|
|||
|
||||
namespace Ulmus\Adapter;
|
||||
|
||||
use Ulmus\Entity\InformationSchema\Table;
|
||||
use Ulmus\QueryBuilder;
|
||||
use Ulmus\Repository;
|
||||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Migration\FieldDefinition;
|
||||
|
||||
class MySQL implements AdapterInterface {
|
||||
use DefaultAdapterTrait;
|
||||
|
||||
const DSN_PREFIX = "mysql";
|
||||
|
||||
public string $hostname;
|
||||
|
||||
|
@ -17,7 +24,7 @@ class MySQL implements AdapterInterface {
|
|||
public string $password;
|
||||
|
||||
public string $charset = "utf8mb4";
|
||||
|
||||
|
||||
public ? string $socket;
|
||||
|
||||
public int $port = 3306;
|
||||
|
@ -68,6 +75,9 @@ class MySQL implements AdapterInterface {
|
|||
catch(PDOException $ex){
|
||||
throw $ex;
|
||||
}
|
||||
finally {
|
||||
$this->password = str_repeat('*', strlen($this->password));
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
@ -91,7 +101,7 @@ class MySQL implements AdapterInterface {
|
|||
$parts[] = "charset={$this->charset}";
|
||||
}
|
||||
|
||||
return "mysql:" . implode(';', $parts);
|
||||
return static::DSN_PREFIX . ":" . implode(';', $parts);
|
||||
}
|
||||
|
||||
public function setup(array $configuration) : void
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Adapter;
|
||||
|
||||
use Ulmus\Common\PdoObject;
|
||||
|
||||
use Ulmus\Entity\Sqlite\Table;
|
||||
use Ulmus\Exception\AdapterConfigurationException;
|
||||
use Ulmus\Migration\FieldDefinition;
|
||||
use Ulmus\{ Repository, QueryBuilder };
|
||||
|
||||
class SQLite implements AdapterInterface {
|
||||
use DefaultAdapterTrait;
|
||||
|
||||
const DSN_PREFIX = "sqlite";
|
||||
|
||||
public string $path;
|
||||
|
||||
public array $pragma;
|
||||
|
||||
public function __construct(
|
||||
? string $path = null,
|
||||
? array $pragma = null
|
||||
) {
|
||||
if ($path !== null) {
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
if ($pragma !== null) {
|
||||
$this->pragma = $pragma;
|
||||
}
|
||||
}
|
||||
|
||||
public function connect() : PdoObject
|
||||
{
|
||||
try {
|
||||
$pdo = new PdoObject($this->buildDataSourceName(), null, null);
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
|
||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
|
||||
}
|
||||
catch(PDOException $ex){
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
public function buildDataSourceName() : string
|
||||
{
|
||||
$parts[] = $this->path;
|
||||
|
||||
return static::DSN_PREFIX . ":" . implode(';', $parts);
|
||||
}
|
||||
|
||||
public function setup(array $configuration) : void
|
||||
{
|
||||
$this->path = $configuration['path'] ?? "";
|
||||
$this->pragma = $configuration['pragma'] ?? [];
|
||||
}
|
||||
|
||||
# https://sqlite.org/lang_keywords.html
|
||||
public function escapeIdentifier(string $segment, int $type) : string
|
||||
{
|
||||
switch($type) {
|
||||
case static::IDENTIFIER_DATABASE:
|
||||
case static::IDENTIFIER_TABLE:
|
||||
case static::IDENTIFIER_FIELD:
|
||||
return '"' . trim(str_replace('"', '""', $segment), '"') . '"';
|
||||
|
||||
case static::IDENTIFIER_VALUE:
|
||||
return "'$segment'";
|
||||
}
|
||||
}
|
||||
|
||||
public function defaultEngine(): ? string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function databaseName() : string
|
||||
{
|
||||
$base = basename($this->path);
|
||||
|
||||
return substr($base, 0, strrpos($base, '.') ?: strlen($base));
|
||||
}
|
||||
|
||||
public function schemaTable(string $databaseName, string $tableName) /* : ? object */
|
||||
{
|
||||
return Table::repository()->loadOneFromField(Table::field('tableName'), $tableName);
|
||||
}
|
||||
|
||||
public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string
|
||||
{
|
||||
$type = $field->type;
|
||||
|
||||
$length = $field->length;
|
||||
|
||||
if ( is_a($type, Entity\Field\Date::class, true) || is_a($type, Entity\Field\Time::class, true) || is_a($type, \DateTime::class, true) ) {
|
||||
$type = "TEXT";
|
||||
$length = strlen((string) $type);
|
||||
}
|
||||
else {
|
||||
switch($type) {
|
||||
case "bool":
|
||||
$check = sprintf("CHECK (%s IN (0, 1))", $field->getColumnName());
|
||||
|
||||
case "bigint":
|
||||
case "int":
|
||||
$type = "INTEGER";
|
||||
break;
|
||||
|
||||
case "array":
|
||||
case "string":
|
||||
$type = "TEXT";
|
||||
$length = null;
|
||||
break;
|
||||
|
||||
case "float":
|
||||
$type = "REAL";
|
||||
break;
|
||||
|
||||
default:
|
||||
$type = "BLOB";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $typeOnly ? $type : $type . ( $length ? "($length" . ( $precision ? ",$precision" : "" ) . ")" : "" );
|
||||
}
|
||||
|
||||
public function tableSyntax() : array
|
||||
{
|
||||
return [
|
||||
'ai' => "AUTOINCREMENT",
|
||||
'pk' => "PRIMARY KEY",
|
||||
'unsigned' => "",
|
||||
];
|
||||
}
|
||||
}
|
|
@ -50,9 +50,9 @@ class EntityField implements WhereRawParameter
|
|||
return false;
|
||||
}
|
||||
|
||||
public static function generateCreateColumn($field) : string
|
||||
public static function generateCreateColumn(AdapterInterface $adapter, $field) : string
|
||||
{
|
||||
$definition = new FieldDefinition($field);
|
||||
$definition = new FieldDefinition($adapter, $field);
|
||||
|
||||
return implode(" ", [
|
||||
$definition->getSqlName(),
|
||||
|
|
|
@ -164,7 +164,7 @@ class EntityResolver {
|
|||
|
||||
public function databaseName() : ? string
|
||||
{
|
||||
return $this->tableAnnotation(false)->database ?? $this->databaseAdapter()->adapter()->database ?? null;
|
||||
return $this->tableAnnotation(false)->database ?? $this->databaseAdapter()->adapter()->databaseName() ?? null;
|
||||
}
|
||||
|
||||
public function sqlAdapter() : \Ulmus\ConnectionAdapter
|
||||
|
|
|
@ -37,6 +37,8 @@ class ConnectionAdapter
|
|||
}
|
||||
|
||||
$this->adapter->setup($connection);
|
||||
|
||||
unset($this->configuration['connections'][$this->name]);
|
||||
}
|
||||
|
||||
public function getConfiguration() : array
|
||||
|
@ -77,7 +79,7 @@ class ConnectionAdapter
|
|||
*/
|
||||
protected function instanciateAdapter($name) : AdapterInterface
|
||||
{
|
||||
$class = substr($name, 0, 2) === "\\" ? $name : "\\Ulmus\\Adapter\\$name";
|
||||
$class = substr($name, 0, 2) === "\\" ? $name : sprintf("\\%s\\Adapter\\%s", __NAMESPACE__, $name);
|
||||
|
||||
return new $class();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Entity\Sqlite;
|
||||
|
||||
use Ulmus\EntityCollection;
|
||||
|
||||
/**
|
||||
* @Table('name' => "sqlite_master")
|
||||
*/
|
||||
class Schema
|
||||
{
|
||||
use \Ulmus\EntityTrait;
|
||||
|
||||
/**
|
||||
* @Id
|
||||
*/
|
||||
public ? string $name;
|
||||
|
||||
/**
|
||||
* @Field
|
||||
*/
|
||||
public ? string $type;
|
||||
|
||||
/**
|
||||
* @Field('name' => 'tbl_name')
|
||||
*/
|
||||
public ? string $tableName;
|
||||
|
||||
/**
|
||||
* @Field
|
||||
*/
|
||||
public ? int $rootpage;
|
||||
|
||||
/**
|
||||
* @Field
|
||||
*/
|
||||
public ? string $sql;
|
||||
|
||||
/**
|
||||
* @Relation('oneToMany', 'key' => 'tableName', 'foreignKey' => 'tableName', 'entity' => Schema::class)
|
||||
*/
|
||||
public EntityCollection $columns;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Entity\Sqlite;
|
||||
|
||||
use Ulmus\Repository;
|
||||
|
||||
class Table extends Schema
|
||||
{
|
||||
public static function repository(string $alias = Repository::DEFAULT_ALIAS): Repository
|
||||
{
|
||||
return new class(static::class, $alias) extends Repository\SqliteRepository
|
||||
{
|
||||
public function finalizeQuery(): void
|
||||
{
|
||||
$this->select(Table::field('tableName'))->groupBy(Table::field('tableName'));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -146,7 +146,12 @@ class EntityCollection extends \ArrayObject {
|
|||
return $this->filtersCollection(fn($obj) => is_a($obj, $className));
|
||||
}
|
||||
|
||||
public function column($field, bool $unique = false) : array
|
||||
public function column($field, bool $uniqueValue = false, $keyField = null) : array
|
||||
{
|
||||
return $this->map($field, $keyField, $uniqueValue);
|
||||
}
|
||||
|
||||
public function map($field, $keyField = null, bool $uniqueValue = false) : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
|
@ -158,11 +163,23 @@ class EntityCollection extends \ArrayObject {
|
|||
$value = $item->$field;
|
||||
}
|
||||
|
||||
if ($unique && in_array($value, $list, true)) {
|
||||
if ($uniqueValue && in_array($value, $list, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$list[] = $value;
|
||||
if ( $keyField !== null ) {
|
||||
if ( is_callable($keyField) ) {
|
||||
$key = call_user_func_array($keyField, [ $item ]);
|
||||
}
|
||||
else {
|
||||
$key = $item->$keyField;
|
||||
}
|
||||
|
||||
$list[$key] = $value;
|
||||
}
|
||||
else {
|
||||
$list[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Ulmus\Migration;
|
||||
|
||||
use Ulmus\Adapter\AdapterInterface;
|
||||
use Ulmus\Annotation\Property\Field;
|
||||
use Ulmus\Entity;
|
||||
|
||||
|
@ -25,8 +26,12 @@ class FieldDefinition {
|
|||
|
||||
public ? string $update;
|
||||
|
||||
public function __construct(array $data)
|
||||
public AdapterInterface $adapter;
|
||||
|
||||
public function __construct(AdapterInterface $adapter, array $data)
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
|
||||
$this->name = $data['name'];
|
||||
$this->builtIn = $data['builtin'];
|
||||
$this->tags = $data['tags'];
|
||||
|
@ -46,108 +51,56 @@ class FieldDefinition {
|
|||
|
||||
public function getSqlType(bool $typeOnly = false) : string
|
||||
{
|
||||
$type = $this->type;
|
||||
|
||||
$length = $this->length;
|
||||
|
||||
if ( is_a($type, Entity\Field\Date::class, true) ) {
|
||||
$type = "DATE";
|
||||
}
|
||||
elseif ( is_a($type, Entity\Field\Time::class, true) ) {
|
||||
$type = "TIME";
|
||||
}
|
||||
elseif ( is_a($type, \DateTime::class, true) ) {
|
||||
$type = "DATETIME";
|
||||
}
|
||||
|
||||
switch($type) {
|
||||
case "bool":
|
||||
$type = "TINYINT";
|
||||
$length = 1;
|
||||
break;
|
||||
|
||||
case "array":
|
||||
case "string":
|
||||
if ($length && $length <= 255) {
|
||||
$type = "VARCHAR";
|
||||
break;
|
||||
}
|
||||
elseif (! $length || ( $length <= 65535 ) ) {
|
||||
$type = "TEXT";
|
||||
}
|
||||
elseif ( $length <= 16777215 ) {
|
||||
$type = "MEDIUMTEXT";
|
||||
}
|
||||
elseif ($length <= 4294967295) {
|
||||
$type = "LONGTEXT";
|
||||
}
|
||||
else {
|
||||
throw new \Exception("A column with size bigger than 4GB cannot be created.");
|
||||
}
|
||||
|
||||
# Length is unnecessary on TEXT fields
|
||||
unset($length);
|
||||
|
||||
break;
|
||||
|
||||
case "float":
|
||||
$type = "DOUBLE";
|
||||
break;
|
||||
|
||||
default:
|
||||
$type = strtoupper($type);
|
||||
break;
|
||||
}
|
||||
|
||||
return $typeOnly ? $type : $type . ( $length ? "($length" . ( $precision ? ",$precision" : "" ) . ")" : "" );
|
||||
return $this->adapter->mapFieldType($this, $typeOnly);
|
||||
}
|
||||
|
||||
public function getSqlParams() : string
|
||||
{
|
||||
$default = $this->getDefault();
|
||||
$syntax = $this->adapter->tableSyntax();
|
||||
|
||||
return implode(' ', array_filter([
|
||||
$this->isUnsigned() ? "UNSIGNED" : null,
|
||||
$this->isUnsigned() ? $syntax['unsigned'] : null,
|
||||
$this->nullable ? "NULL" : "NOT NULL",
|
||||
$default ? "DEFAULT $default" : null,
|
||||
$this->isAutoIncrement() ? "AUTO_INCREMENT" : null,
|
||||
$this->isPrimaryKey() ? "PRIMARY KEY" : null,
|
||||
$this->isPrimaryKey() ? $syntax['pk'] : null,
|
||||
$this->isAutoIncrement() ? $syntax['ai'] : null,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function getFieldTag() : ? Field
|
||||
public function getFieldTag() : ? Field
|
||||
{
|
||||
$field = array_filter($this->tags, fn($item) => $item['object'] instanceof Field);
|
||||
|
||||
return array_pop($field)['object'];
|
||||
}
|
||||
|
||||
protected function getColumnName() : ? string
|
||||
public function getColumnName() : ? string
|
||||
{
|
||||
return $this->getFieldTag()->name ?? $this->name;
|
||||
}
|
||||
|
||||
protected function getDefault() : ? string
|
||||
public function getDefault() : ? string
|
||||
{
|
||||
return $this->getFieldTag()->attributes['default'] ?? ( $this->nullable ? "NULL" : null ) ;
|
||||
}
|
||||
|
||||
protected function isPrimaryKey() : bool
|
||||
public function isPrimaryKey() : bool
|
||||
{
|
||||
return $this->getFieldTag()->attributes['primary_key'] ?? false;
|
||||
}
|
||||
|
||||
protected function isAutoIncrement() : bool
|
||||
public function isAutoIncrement() : bool
|
||||
{
|
||||
return $this->getFieldTag()->attributes['auto_increment'] ?? false;
|
||||
}
|
||||
|
||||
protected function isUnique() : bool
|
||||
public function isUnique() : bool
|
||||
{
|
||||
return $this->getFieldTag()->attributes['unique'] ?? false;
|
||||
}
|
||||
|
||||
protected function isUnsigned() : bool
|
||||
public function isUnsigned() : bool
|
||||
{
|
||||
return $this->getFieldTag()->attributes['unsigned'] ?? false;
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Modeler;
|
||||
|
||||
class Schema {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function compare()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function migrate()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Ulmus\Query;
|
||||
|
||||
use Ulmus\Adapter\AdapterInterface;
|
||||
use Ulmus\Annotation,
|
||||
Ulmus\Common\EntityField;
|
||||
|
||||
|
@ -17,7 +18,13 @@ class Alter extends Fragment {
|
|||
|
||||
public bool $skipExisting = true;
|
||||
|
||||
public array $fieldList;
|
||||
public array $fieldList;
|
||||
|
||||
public AdapterInterface $adapter;
|
||||
|
||||
public function __construct(AdapterInterface $adapter) {
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
|
@ -29,7 +36,7 @@ class Alter extends Fragment {
|
|||
public function renderFields() : string
|
||||
{
|
||||
return "(" . PHP_EOL . implode("," . PHP_EOL, array_map(function($field) {
|
||||
return " " . EntityField::generateAlterColumn($field);
|
||||
return " " . EntityField::generateAlterColumn($this->adapter, $field);
|
||||
}, $this->fieldList)) . PHP_EOL . ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Ulmus\Query;
|
||||
|
||||
use Ulmus\Adapter\AdapterInterface;
|
||||
use Ulmus\Annotation,
|
||||
Ulmus\Common\EntityField;
|
||||
|
||||
|
@ -19,6 +20,12 @@ class Create extends Fragment {
|
|||
|
||||
public array $fieldList;
|
||||
|
||||
public AdapterInterface $adapter;
|
||||
|
||||
public function __construct(AdapterInterface $adapter) {
|
||||
$this->adapter = $adapter;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
return $this->renderSegments([
|
||||
|
@ -29,7 +36,7 @@ class Create extends Fragment {
|
|||
public function renderFields() : string
|
||||
{
|
||||
return "(" . PHP_EOL . implode("," . PHP_EOL, array_map(function($field) {
|
||||
return " " . EntityField::generateCreateColumn($field);
|
||||
return " " . EntityField::generateCreateColumn($this->adapter, $field);
|
||||
}, $this->fieldList)) . PHP_EOL . ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use Ulmus\Repository\ConditionTrait;
|
|||
class Join extends Fragment
|
||||
{
|
||||
use ConditionTrait;
|
||||
|
||||
|
||||
const SQL_OUTER = "OUTER";
|
||||
const SQL_TOKEN = "JOIN";
|
||||
|
||||
|
@ -39,9 +39,12 @@ class Join extends Fragment
|
|||
|
||||
public int $order = 40;
|
||||
|
||||
public int $joinOrder = 0;
|
||||
|
||||
public function __construct(QueryBuilderInterface $queryBuilder)
|
||||
{
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
$cls = get_class($queryBuilder);
|
||||
$this->queryBuilder = new $cls();
|
||||
$this->queryBuilder->parent = $queryBuilder;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ class Select extends Fragment {
|
|||
if ( ! is_numeric($key) ) {
|
||||
$value = sprintf(static::FIELD_AS, $value, $key);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Query\Sqlite;
|
||||
|
||||
class Pragma extends \Ulmus\Query\Fragment {
|
||||
|
||||
const SQL_TOKEN = "PRAGMA %s%s";
|
||||
|
||||
public int $order = 15;
|
||||
|
||||
public /* object|string */ $pragma;
|
||||
|
||||
public $value;
|
||||
|
||||
public bool $callable;
|
||||
|
||||
public function set(/* object|Stringable */ $pragma, /* ? mixed */ $value = null, bool $callable = false) : self
|
||||
{
|
||||
$this->pragma = $pragma;
|
||||
|
||||
if ($value) {
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
$this->callable = $callable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() : string
|
||||
{
|
||||
if ( isset($this->value) ) {
|
||||
$value = sprintf($this->callable ? " (%s)" : " = %s", $this->value);
|
||||
}
|
||||
|
||||
return $this->renderSegments([
|
||||
sprintf(static::SQL_TOKEN, $this->pragma, $value ?? "")
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -305,7 +305,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$join->outer = $outer;
|
||||
|
||||
$join->alias = $alias;
|
||||
|
||||
|
||||
return $join;
|
||||
}
|
||||
|
||||
|
@ -328,7 +328,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function create(array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||
public function create(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||
{
|
||||
if ( null === $this->getFragment(Query\Create::class) ) {
|
||||
if ( $schema ) {
|
||||
|
@ -339,7 +339,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$table = "$database.$table";
|
||||
}
|
||||
|
||||
$create = new Query\Create();
|
||||
$create = new Query\Create($adapter);
|
||||
$this->push($create);
|
||||
|
||||
$create->fieldList = $fieldlist;
|
||||
|
@ -352,7 +352,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function alter(array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||
public function alter(Adapter\AdapterInterface $adapter, array $fieldlist, string $table, ? string $database = null, ? string $schema = null) : self
|
||||
{
|
||||
if ( null === $this->getFragment(Query\Create::class) ) {
|
||||
if ( $schema ) {
|
||||
|
@ -363,7 +363,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$table = "$database.$table";
|
||||
}
|
||||
|
||||
$alter = new Query\Alter();
|
||||
$alter = new Query\Alter($adapter);
|
||||
$this->push($alter);
|
||||
|
||||
$alter->fieldList = $fieldlist;
|
||||
|
@ -405,7 +405,7 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
$sql = [];
|
||||
|
||||
usort($this->queryStack, function($q1, $q2) {
|
||||
return $q1->order <=> $q2->order;
|
||||
return (float) $q1->order <=> (float) $q2->order;
|
||||
});
|
||||
|
||||
foreach($this->queryStack as $fragment) {
|
||||
|
@ -456,9 +456,9 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
}
|
||||
}
|
||||
|
||||
public function getFragments() : array
|
||||
public function getFragments(/*? Query\Fragment|array*/ $fragment = null) : array
|
||||
{
|
||||
return $this->queryStack;
|
||||
return $fragment === null ? array_filter($this->queryStack, fn($e) => get_class($e) === $fragment) : $this->queryStack;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
|
@ -485,4 +485,15 @@ class QueryBuilder implements Query\QueryBuilderInterface
|
|||
{
|
||||
$this->values = $values;
|
||||
}
|
||||
|
||||
protected function nextJoinOrder() : float
|
||||
{
|
||||
$next = 0;
|
||||
|
||||
foreach($this->getFragments(Query\Join::class) as $join) {
|
||||
$next = max($next, $join->joinOrder - Query\Join::ORDER_VALUE);
|
||||
}
|
||||
|
||||
return $next + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\QueryBuilder;
|
||||
|
||||
use Ulmus\QueryBuilder;
|
||||
|
||||
use Ulmus\Query;
|
||||
|
||||
class MssqlQueryBuilder extends QueryBuilder implements Ulmus\Query\QueryBuilderInterface
|
||||
{
|
||||
public function limit(int $value) : self
|
||||
{
|
||||
if ( null === $offset = $this->getFragment(Query\MsSQL\Offset::class) ) {
|
||||
$offset = new Query\MsSQL\Offset();
|
||||
$this->push($offset);
|
||||
}
|
||||
|
||||
$offset->limit = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function offset(int $value) : self
|
||||
{
|
||||
if ( null === $offset = $this->getFragment(Query\MsSQL\Offset::class) ) {
|
||||
$offset = new Query\MsSQL\Offset();
|
||||
$this->push($offset);
|
||||
}
|
||||
|
||||
$offset->offset = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\QueryBuilder;
|
||||
|
||||
use Ulmus\QueryBuilder;
|
||||
|
||||
use Ulmus\Query;
|
||||
|
||||
class SqliteQueryBuilder extends QueryBuilder implements Query\QueryBuilderInterface
|
||||
{
|
||||
public function pragma(/*object|Stringable*/ $name, $value = null, bool $callable = false) : self
|
||||
{
|
||||
if ( null !== ( $pragma = $this->getFragment(Query\Sqlite\Pragma::class) ) ) {
|
||||
$pragma->set($name, $value, $callable);
|
||||
}
|
||||
else {
|
||||
$pragma = new Query\Sqlite\Pragma();
|
||||
$pragma->set($name, $value, $callable);
|
||||
$this->push($pragma);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ use Ulmus\Common\EntityResolver;
|
|||
|
||||
class Repository
|
||||
{
|
||||
use EventTrait, Repository\ConditionTrait;
|
||||
use EventTrait, Repository\ConditionTrait, Repository\EscapeTrait;
|
||||
|
||||
const DEFAULT_ALIAS = "this";
|
||||
|
||||
|
@ -28,9 +28,9 @@ class Repository
|
|||
$this->alias = $alias;
|
||||
$this->entityResolver = Ulmus::resolveEntity($entity);
|
||||
$this->adapter = $adapter ?? $this->entityResolver->databaseAdapter();
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
$this->queryBuilder = Ulmus::queryBuilder($entity);
|
||||
}
|
||||
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
#$this->queryBuilder = clone $this->queryBuilder;
|
||||
|
@ -148,9 +148,9 @@ class Repository
|
|||
# $dataset = array_filter($dataset, fn($item, $field) => ! ($this->entityResolver->searchFieldAnnotation($field, new Field, false)->readonly ?? false), \ARRAY_FILTER_USE_BOTH);
|
||||
|
||||
$statement = $this->insertSqlQuery($fieldsAndValue ?? $dataset, $replace)->runInsertQuery();
|
||||
|
||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||
( null !== $primaryKeyDefinition )) {
|
||||
|
||||
if ( ( 0 !== $statement->lastInsertId ) &&
|
||||
( null !== $primaryKeyDefinition )) {
|
||||
|
||||
$pkField = key($primaryKeyDefinition);
|
||||
$dataset[$pkField] = $statement->lastInsertId;
|
||||
|
@ -195,74 +195,20 @@ class Repository
|
|||
|
||||
}
|
||||
|
||||
public function loadCollectionRelation(EntityCollection $collection, /*array|string*/ $fields) : void
|
||||
{
|
||||
foreach ((array)$fields as $name) {
|
||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation()))) {
|
||||
$relationType = strtolower(str_replace(['-', '_', ' '], '', $relation->type));
|
||||
|
||||
$order = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\OrderBy());
|
||||
$where = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\Where());
|
||||
|
||||
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type'];
|
||||
$baseEntityResolver = $baseEntity::resolveEntity();
|
||||
|
||||
$property = ($baseEntityResolver->field($relation->foreignKey, 01, false) ?: $baseEntityResolver->field($relation->foreignKey, 02))['name'];
|
||||
$entityProperty = ($this->entityResolver->field($relation->key, 01, false) ?: $this->entityResolver->field($relation->key, 02))['name'];
|
||||
|
||||
$repository = $baseEntity::repository();
|
||||
|
||||
foreach ($where as $condition) {
|
||||
$repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [$this]) : $condition->value, $condition->operator, $condition->condition);
|
||||
}
|
||||
|
||||
foreach ($order as $item) {
|
||||
$repository->orderBy($item->field, $item->order);
|
||||
}
|
||||
|
||||
$field = $relation->key;
|
||||
|
||||
$values = [];
|
||||
|
||||
$key = is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey);
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$values[] = is_callable($field) ? $field($item) : $item->$entityProperty;
|
||||
}
|
||||
|
||||
$repository->where($key, $values);
|
||||
|
||||
switch ($relationType) {
|
||||
case 'onetoone':
|
||||
$results = call_user_func([$repository, "loadOne"]);
|
||||
$item->$name = $results ?: new $baseEntity();
|
||||
|
||||
break;
|
||||
|
||||
case 'onetomany':
|
||||
$results = call_user_func([$repository, $relation->function]);
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$item->$name = $baseEntity::entityCollection();
|
||||
$item->$name->mergeWith($results->filtersCollection(fn($e) => $e->$property === $item->$entityProperty));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function replace(/*object|array*/ $entity, ? array $fieldsAndValue = null) : bool
|
||||
{
|
||||
return $this->save($entity, $fieldsAndValue, true);
|
||||
}
|
||||
|
||||
public function replaceAll(/*EntityCollection|array*/ $collection) : void
|
||||
public function replaceAll(/*EntityCollection|array*/ $collection) : int
|
||||
{
|
||||
$changed = 0;
|
||||
|
||||
foreach($collection as $entity) {
|
||||
$this->replace($entity);
|
||||
$this->replace($entity) && $changed++;
|
||||
}
|
||||
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function truncate(? string $table = null, ? string $alias = null, ? string $schema = null) : self
|
||||
|
@ -489,10 +435,10 @@ class Repository
|
|||
public function randomizeOrder() : self
|
||||
{
|
||||
$this->queryBuilder->orderBy(Common\Sql::function('RAND', Sql::identifier('CURDATE()+0')));
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function orders(array $orderList) : self
|
||||
{
|
||||
foreach($orderList as $field => $direction) {
|
||||
|
@ -678,6 +624,64 @@ class Repository
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function loadCollectionRelation(EntityCollection $collection, /*array|string*/ $fields) : void
|
||||
{
|
||||
foreach ((array)$fields as $name) {
|
||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, new Annotation\Property\Relation()))) {
|
||||
$relationType = strtolower(str_replace(['-', '_', ' '], '', $relation->type));
|
||||
|
||||
$order = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\OrderBy());
|
||||
$where = $this->entityResolver->searchFieldAnnotationList($name, new Annotation\Property\Where());
|
||||
|
||||
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type'];
|
||||
$baseEntityResolver = $baseEntity::resolveEntity();
|
||||
|
||||
$property = ($baseEntityResolver->field($relation->foreignKey, 01, false) ?: $baseEntityResolver->field($relation->foreignKey, 02))['name'];
|
||||
$entityProperty = ($this->entityResolver->field($relation->key, 01, false) ?: $this->entityResolver->field($relation->key, 02))['name'];
|
||||
|
||||
$repository = $baseEntity::repository();
|
||||
|
||||
foreach ($where as $condition) {
|
||||
$repository->where($condition->field, is_callable($condition->value) ? call_user_func_array($condition->value, [$this]) : $condition->value, $condition->operator, $condition->condition);
|
||||
}
|
||||
|
||||
foreach ($order as $item) {
|
||||
$repository->orderBy($item->field, $item->order);
|
||||
}
|
||||
|
||||
$field = $relation->key;
|
||||
|
||||
$values = [];
|
||||
|
||||
$key = is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey);
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$values[] = is_callable($field) ? $field($item) : $item->$entityProperty;
|
||||
}
|
||||
|
||||
$repository->where($key, $values);
|
||||
|
||||
switch ($relationType) {
|
||||
case 'onetoone':
|
||||
$results = call_user_func([$repository, "loadOne"]);
|
||||
$item->$name = $results ?: new $baseEntity();
|
||||
|
||||
break;
|
||||
|
||||
case 'onetomany':
|
||||
$results = call_user_func([$repository, $relation->function]);
|
||||
|
||||
foreach ($collection as $item) {
|
||||
$item->$name = $baseEntity::entityCollection();
|
||||
$item->$name->mergeWith($results->filtersCollection(fn($e) => $e->$property === $item->$entityProperty));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self
|
||||
{
|
||||
if ($count) {
|
||||
|
@ -819,13 +823,7 @@ class Repository
|
|||
public function createSqlQuery() : self
|
||||
{
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Create::class) ) {
|
||||
$this->queryBuilder->create($this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) {
|
||||
if ( $engine = $this->entityResolver->tableAnnotation()->engine ?? $this->entityResolver->databaseAdapter()->adapter()->defaultEngine() ) {
|
||||
$this->queryBuilder->engine($engine);
|
||||
}
|
||||
$this->queryBuilder->create($this->adapter->adapter(), $this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
@ -834,7 +832,7 @@ class Repository
|
|||
public function alterSqlQuery(array $fields) : self
|
||||
{
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Alter::class) ) {
|
||||
$this->queryBuilder->create($this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
|
||||
$this->queryBuilder->create($this->adapter->adapter(), $this->escapeFieldList($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true)), $this->escapeTable($this->entityResolver->tableName()), $this->entityResolver->schemaName());
|
||||
}
|
||||
|
||||
|
||||
|
@ -859,7 +857,7 @@ class Repository
|
|||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public function instanciateEntityCollection(...$arguments) : EntityCollection
|
||||
{
|
||||
return $this->entityClass::entityCollection(...$arguments);
|
||||
|
@ -870,47 +868,6 @@ class Repository
|
|||
return new $this->entityClass();
|
||||
}
|
||||
|
||||
public function escapeField(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_FIELD);
|
||||
}
|
||||
|
||||
public function escapeFieldList(array $fieldList) : array
|
||||
{
|
||||
foreach($fieldList as &$list) {
|
||||
$list['name'] = $this->escapeField($list['name']);
|
||||
|
||||
$fieldTag = array_filter($list['tags'] ?? [], fn($item) => $item['object'] instanceof Field)[0]['object'] ?? null;
|
||||
|
||||
if ( $fieldTag && isset($fieldTag->name) ) {
|
||||
$fieldTag->name = $this->escapeField($fieldTag->name);
|
||||
}
|
||||
}
|
||||
|
||||
return $fieldList;
|
||||
}
|
||||
|
||||
public function escapeTable(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_TABLE);
|
||||
}
|
||||
|
||||
public function escapeDatabase(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_DATABASE);
|
||||
}
|
||||
|
||||
public function escapedDatabase() : ? string
|
||||
{
|
||||
$name = $this->entityResolver->tableAnnotation()->database ?? $this->adapter->adapter()->database ?? null;
|
||||
|
||||
return $name ? static::escapeDatabase($name) : null;
|
||||
}
|
||||
|
||||
public function escapeSchema(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_SCHEMA);
|
||||
}
|
||||
|
||||
public function hasFilters() : bool
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\{ Query, Adapter };
|
||||
use Ulmus\Annotation\Property\Field;
|
||||
|
||||
trait EscapeTrait
|
||||
{
|
||||
public function escapeField(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_FIELD);
|
||||
}
|
||||
|
||||
public function escapeFieldList(array $fieldList) : array
|
||||
{
|
||||
foreach($fieldList as &$list) {
|
||||
$list['name'] = $this->escapeField($list['name']);
|
||||
|
||||
$fieldTag = array_filter($list['tags'] ?? [], fn($item) => $item['object'] instanceof Field)[0]['object'] ?? null;
|
||||
|
||||
if ( $fieldTag && isset($fieldTag->name) ) {
|
||||
$fieldTag->name = $this->escapeField($fieldTag->name);
|
||||
}
|
||||
}
|
||||
|
||||
return $fieldList;
|
||||
}
|
||||
|
||||
public function escapeTable(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_TABLE);
|
||||
}
|
||||
|
||||
public function escapeDatabase(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_DATABASE);
|
||||
}
|
||||
|
||||
public function escapedDatabase() : ? string
|
||||
{
|
||||
$name = $this->entityResolver->tableAnnotation()->database ?? $this->adapter->adapter()->database ?? null;
|
||||
|
||||
return $name ? static::escapeDatabase($name) : null;
|
||||
}
|
||||
|
||||
public function escapeSchema(string $identifier) : string
|
||||
{
|
||||
return $this->adapter->adapter()->escapeIdentifier($identifier, Adapter\AdapterInterface::IDENTIFIER_SCHEMA);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace Ulmus\Repository;
|
|||
use Ulmus\{ Repository, Query };
|
||||
|
||||
class MssqlRepository extends Repository {
|
||||
|
||||
/*
|
||||
protected function finalizeQuery() : void
|
||||
{
|
||||
if ( null !== $offset = $this->queryBuilder->getFragment(Query\Offset::class) ) {
|
||||
|
@ -39,5 +39,34 @@ class MssqlRepository extends Repository {
|
|||
$this->queryBuilder->removeFragment($limit);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
protected function finalizeQuery() : void
|
||||
{
|
||||
if ( null !== $offset = $this->queryBuilder->getFragment(Query\MsSQL\Offset::class) ) {
|
||||
# an order by is mandatory for mssql offset/limit
|
||||
if ( null === $order = $this->queryBuilder->getFragment(Query\OrderBy::class) ) {
|
||||
$this->orderBy("(SELECT 0)");
|
||||
}
|
||||
|
||||
if ( empty ($offset->offset ) ) {
|
||||
if ( null !== $select = $this->queryBuilder->getFragment(Query\Select::class) ) {
|
||||
$select->top = $offset->limit;
|
||||
}
|
||||
elseif ( null !== $delete = $this->queryBuilder->getFragment(Query\Delete::class) ) {
|
||||
$delete->top = $offset->limit;
|
||||
}
|
||||
|
||||
$this->queryBuilder->removeFragment($offset);
|
||||
}
|
||||
elseif ( empty($offset->limit) ) {
|
||||
throw new \Exception("Your offset query fragment is missing a LIMIT value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function serverRequestCountRepository() : Repository
|
||||
{
|
||||
return new static($this->entityClass, $this->alias, $this->adapter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\{Common\EntityResolver, ConnectionAdapter, QueryBuilder, Repository, Query, Ulmus};
|
||||
|
||||
class MysqlRepository extends Repository {
|
||||
|
||||
public function pragma(/*object|Stringable*/ $pragma, $argument = null, bool $callable = false) : self
|
||||
{
|
||||
$this->queryBuilder->pragma($pragma, $argument, $callable);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function createSqlQuery() : self
|
||||
{
|
||||
if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) {
|
||||
if ( $engine = $this->entityResolver->tableAnnotation()->engine ?? $this->entityResolver->databaseAdapter()->adapter()->defaultEngine() ) {
|
||||
$this->queryBuilder->engine($engine);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::createSqlQuery();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Ulmus\Repository;
|
||||
|
||||
use Ulmus\{ConnectionAdapter, QueryBuilder, Repository, Query, Ulmus};
|
||||
|
||||
class SqliteRepository extends Repository {
|
||||
|
||||
public function pragma(/*object|Stringable*/ $pragma, $argument = null, bool $callable = false) : self
|
||||
{
|
||||
$this->queryBuilder->pragma($pragma, $argument, $callable);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function finalizeQuery() : void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function serverRequestCountRepository() : Repository
|
||||
{
|
||||
return new static($this->entityClass, $this->alias, $this->adapter);
|
||||
}
|
||||
}
|
|
@ -41,4 +41,10 @@ trait SearchRequestPaginationTrait {
|
|||
{
|
||||
return $this->pageCount() > 1;
|
||||
}
|
||||
public function skipCount(bool $value) : self
|
||||
{
|
||||
$this->skipCount = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,14 +102,18 @@ abstract class Ulmus
|
|||
return static::$resolved[$entityClass] ?? static::$resolved[$entityClass] = new Common\EntityResolver($entityClass);
|
||||
}
|
||||
|
||||
public static function repository(...$arguments) : Repository
|
||||
public static function repository(string $entityClass, ...$arguments) : Repository
|
||||
{
|
||||
return new static::$repositoryClass(...$arguments);
|
||||
$cls = $entityClass::resolveEntity()->sqlAdapter()->adapter()->repositoryClass();
|
||||
|
||||
return new $cls($entityClass, ...$arguments);
|
||||
}
|
||||
|
||||
public static function queryBuilder(...$arguments) : Query\QueryBuilderInterface
|
||||
public static function queryBuilder($entityClass, ...$arguments) : Query\QueryBuilderInterface
|
||||
{
|
||||
return new static::$queryBuilderClass(...$arguments);
|
||||
$cls = $entityClass::resolveEntity()->sqlAdapter()->adapter()->queryBuilderClass();
|
||||
|
||||
return new $cls(...$arguments);
|
||||
}
|
||||
|
||||
public static function instanciateObject(string $type, array $arguments) : object
|
||||
|
|
Loading…
Reference in New Issue