- WIP on Migration of MsSQL
This commit is contained in:
parent
c54de30069
commit
fb4985160a
@ -1,121 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Ulmus\Adapter;
|
|
||||||
|
|
||||||
use Ulmus\{ConnectionAdapter, Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder\Sql\MysqlQueryBuilder, Entity};
|
|
||||||
|
|
||||||
UNUSED? trait DefaultAdapterTrait
|
|
||||||
{
|
|
||||||
public function repositoryClass() : string
|
|
||||||
{
|
|
||||||
return Repository::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function queryBuilderClass() : string
|
|
||||||
{
|
|
||||||
return MysqlQueryBuilder::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function tableSyntax() : array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'ai' => "AUTO_INCREMENT",
|
|
||||||
'pk' => "PRIMARY KEY",
|
|
||||||
'unsigned' => "UNSIGNED",
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function databaseName() : string
|
|
||||||
{
|
|
||||||
return $this->database;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object
|
|
||||||
{
|
|
||||||
return Table::repository(Repository::DEFAULT_ALIAS, $adapter)
|
|
||||||
->select(\Ulmus\Common\Sql::raw('this.*'))
|
|
||||||
->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
|
||||||
->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $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 . ( isset($length) ? "($length" . ( ! empty($precision) ? ",$precision" : "" ) . ")" : "" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public function whitelistAttributes(array &$parameters) : void
|
|
||||||
{
|
|
||||||
$parameters = array_intersect_key($parameters, array_flip(static::ALLOWED_ATTRIBUTES));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
|
|
||||||
{
|
|
||||||
if ($field['previous']) {
|
|
||||||
$position = sprintf('AFTER %s', $this->escapeIdentifier($field['previous']['field'], AdapterInterface::IDENTIFIER_FIELD));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$position = "FIRST";
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(" ", [
|
|
||||||
strtoupper($field['action']),
|
|
||||||
$this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD),
|
|
||||||
$definition->getSqlType(),
|
|
||||||
$definition->getSqlParams(),
|
|
||||||
$position,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,8 @@ use Ulmus\Exception\AdapterConfigurationException;
|
|||||||
use Ulmus\Ulmus;
|
use Ulmus\Ulmus;
|
||||||
|
|
||||||
use Ulmus\Migration\FieldDefinition;
|
use Ulmus\Migration\FieldDefinition;
|
||||||
use Ulmus\{Entity\InformationSchema\Table,
|
use Ulmus\{ConnectionAdapter,
|
||||||
|
Entity\InformationSchema\Table,
|
||||||
Migration\MigrateInterface,
|
Migration\MigrateInterface,
|
||||||
Migration\SqlMigrationTrait,
|
Migration\SqlMigrationTrait,
|
||||||
Repository,
|
Repository,
|
||||||
@ -246,4 +247,13 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|||||||
{
|
{
|
||||||
return QueryBuilder\Sql\MssqlQueryBuilder::class;
|
return QueryBuilder\Sql\MssqlQueryBuilder::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object
|
||||||
|
{
|
||||||
|
return \Ulmus\Entity\Mssql\Table::repository(Repository::DEFAULT_ALIAS, $adapter)
|
||||||
|
->select(\Ulmus\Common\Sql::raw('this.*'))
|
||||||
|
->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
||||||
|
->or($this->escapeIdentifier('table_catalog', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
||||||
|
->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,4 +181,26 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
|
|||||||
->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
->where($this->escapeIdentifier('table_schema', AdapterInterface::IDENTIFIER_FIELD), $databaseName)
|
||||||
->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName);
|
->loadOneFromField($this->escapeIdentifier('table_name', AdapterInterface::IDENTIFIER_FIELD), $tableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
|
||||||
|
{
|
||||||
|
if ($field['action'] === 'add') {
|
||||||
|
if (! empty($field['previous'])) {
|
||||||
|
$position = sprintf('AFTER %s', $this->escapeIdentifier($field['previous']->field, AdapterInterface::IDENTIFIER_FIELD));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$position = "FIRST";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(" ", array_filter([
|
||||||
|
strtoupper($field['action']),
|
||||||
|
$this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD),
|
||||||
|
$definition->getSqlType(),
|
||||||
|
$definition->getSqlParams(),
|
||||||
|
$position ?? null,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,5 @@ namespace Ulmus\Attribute\Property;
|
|||||||
class Filter {
|
class Filter {
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $method = ""
|
public string $method = ""
|
||||||
)
|
) {}
|
||||||
{}
|
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
namespace Ulmus\Attribute\Property;
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
use Ulmus\Attribute\Attribute;
|
use Ulmus\Attribute\Attribute;
|
||||||
|
use Ulmus\Repository;
|
||||||
|
|
||||||
#[\Attribute]
|
#[\Attribute]
|
||||||
class Join implements ResettablePropertyInterface {
|
class Join implements ResettablePropertyInterface {
|
||||||
@ -16,4 +17,20 @@ class Join implements ResettablePropertyInterface {
|
|||||||
$this->key = Attribute::handleArrayField($this->key);
|
$this->key = Attribute::handleArrayField($this->key);
|
||||||
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## AWAITING THE Closures in constant expression from PHP 8.5 !
|
||||||
|
public function foreignKey(? Repository $repository = null) : mixed
|
||||||
|
{
|
||||||
|
if (is_string($this->foreignKey)) {
|
||||||
|
if (str_starts_with($this->foreignKey, 'fn(') && str_contains($this->foreignKey, '=>')) {
|
||||||
|
$closure = eval("return {$this->foreignKey};");
|
||||||
|
|
||||||
|
return $repository ? $closure($repository) : $closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->entity::field($this->foreignKey, $this->alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->foreignKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Ulmus\Attribute\Property;
|
namespace Ulmus\Attribute\Property;
|
||||||
|
|
||||||
use Ulmus\Attribute\Attribute;
|
use Ulmus\Attribute\Attribute;
|
||||||
|
use Ulmus\Repository;
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
#[\Attribute(\Attribute::TARGET_PROPERTY)]
|
||||||
class Relation implements ResettablePropertyInterface {
|
class Relation implements ResettablePropertyInterface {
|
||||||
@ -21,6 +22,7 @@ class Relation implements ResettablePropertyInterface {
|
|||||||
public null|string $entity = null,
|
public null|string $entity = null,
|
||||||
public null|string $join = null,
|
public null|string $join = null,
|
||||||
public null|string $function = null,
|
public null|string $function = null,
|
||||||
|
public null|string $alias = null,
|
||||||
) {
|
) {
|
||||||
$this->key = Attribute::handleArrayField($this->key);
|
$this->key = Attribute::handleArrayField($this->key);
|
||||||
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
$this->foreignKey = Attribute::handleArrayField($this->foreignKey);
|
||||||
@ -83,4 +85,20 @@ class Relation implements ResettablePropertyInterface {
|
|||||||
{
|
{
|
||||||
return (bool) $this->bridge;
|
return (bool) $this->bridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## AWAITING THE Closures in constant expression from PHP 8.5 !
|
||||||
|
public function foreignKey(? Repository $repository = null) : mixed
|
||||||
|
{
|
||||||
|
if (is_string($this->foreignKey)) {
|
||||||
|
if (str_starts_with($this->foreignKey, 'fn(') && str_contains($this->foreignKey, '=>')) {
|
||||||
|
$closure = eval("return {$this->foreignKey};");
|
||||||
|
|
||||||
|
return $repository ? $closure($repository) : $closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->entity::field($this->foreignKey, $this->alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->foreignKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ class DatasetHandler
|
|||||||
return $unmatched;
|
return $unmatched;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pullRelation(object $entity) : Generator
|
public function pullRelation(object $entity, bool $onlyPreviouslyLoaded = true) : Generator
|
||||||
{
|
{
|
||||||
foreach($this->entityResolver->reflectedClass->getProperties(true) as $name => $field) {
|
foreach($this->entityResolver->reflectedClass->getProperties(true) as $name => $field) {
|
||||||
$relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] );
|
$relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] );
|
||||||
@ -119,7 +119,9 @@ class DatasetHandler
|
|||||||
if ($relation) {
|
if ($relation) {
|
||||||
$ignore = $this->entityResolver->searchFieldAnnotation($name, [ Relation\Ignore::class ] );
|
$ignore = $this->entityResolver->searchFieldAnnotation($name, [ Relation\Ignore::class ] );
|
||||||
|
|
||||||
if ($ignore && $ignore->ignoreExport) {
|
$load = $onlyPreviouslyLoaded ? (new \ReflectionProperty($entity::class, $name))->isInitialized($entity) : true;
|
||||||
|
|
||||||
|
if ( ! $load || ( $ignore && $ignore->ignoreExport ) ) {
|
||||||
if ( $relation->isOneToOne() ) {
|
if ( $relation->isOneToOne() ) {
|
||||||
# @TODO TO INCLUDED INTO getTypes() RETURNED CLASS WHEN DONE !
|
# @TODO TO INCLUDED INTO getTypes() RETURNED CLASS WHEN DONE !
|
||||||
yield $name => ( new \ReflectionClass($field->getTypes()[0]) )->newInstanceWithoutConstructor();
|
yield $name => ( new \ReflectionClass($field->getTypes()[0]) )->newInstanceWithoutConstructor();
|
||||||
@ -132,9 +134,10 @@ class DatasetHandler
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
# @TODO Must fix recursive bug.. this last check is way too basic to work
|
|
||||||
if ( $entity->__isset($name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
if ( $entity->__isset($name) && ($relation->entity ?? $relation->bridge) !== static::class ) {
|
||||||
if ( null !== $value = $entity->__isset($name) ?? null ) {
|
$value = $entity->$name ?? null;
|
||||||
|
|
||||||
|
if ( null !== $value ) {
|
||||||
if ( is_iterable($value) ) {
|
if ( is_iterable($value) ) {
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
|
@ -69,16 +69,19 @@ class Column
|
|||||||
{
|
{
|
||||||
$nullable = $this->nullable === 'YES';
|
$nullable = $this->nullable === 'YES';
|
||||||
|
|
||||||
|
$definitionValue = $this->getDefinitionValue($definition);
|
||||||
|
|
||||||
if ($nullable !== $definition->allowsNull()) {
|
if ($nullable !== $definition->allowsNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset($definition->value) && $this->canHaveDefaultValue() ) {
|
if ( isset($definitionValue) && $this->canHaveDefaultValue() ) {
|
||||||
if ( $definition->value !== $this->defaultValue()) {
|
if ( $definitionValue !== $this->defaultValue() ) {
|
||||||
|
# dump($definition->value, $this->defaultValue(), $definition->name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif (! isset($definition->value)) {
|
elseif (! isset($definitionValue)) {
|
||||||
if ( ! $this->defaultValueIsNull() ) {
|
if ( ! $this->defaultValueIsNull() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -87,6 +90,12 @@ class Column
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getDefinitionValue(ReflectedProperty $definition) : mixed
|
||||||
|
{
|
||||||
|
# Attribute's value first, then defined in class value
|
||||||
|
return $definition->getAttribute(Field::class)->object->default ?? $definition->value ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
protected function defaultValueIsNull() : bool
|
protected function defaultValueIsNull() : bool
|
||||||
{
|
{
|
||||||
return $this->defaultValue() === null;
|
return $this->defaultValue() === null;
|
||||||
|
73
src/Entity/Mssql/Column.php
Normal file
73
src/Entity/Mssql/Column.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Entity\Mssql;
|
||||||
|
|
||||||
|
use Ulmus\Attribute\Property\Field;
|
||||||
|
use Ulmus\Entity\Mysql\id;
|
||||||
|
|
||||||
|
class Column extends \Ulmus\Entity\InformationSchema\Column
|
||||||
|
{
|
||||||
|
|
||||||
|
# TODO ! Handle FUNCTIONAL default value
|
||||||
|
protected function canHaveDefaultValue(): bool
|
||||||
|
{
|
||||||
|
return ! in_array(strtoupper($this->dataType), [
|
||||||
|
# 'BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB', 'JSON', 'TEXT', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'GEOMETRY'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function defaultValue() : mixed
|
||||||
|
{
|
||||||
|
if ($this->default === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Removing first pairs of brackets
|
||||||
|
$default = $this->isBetween($this->default, '(', ')') ? substr($this->default, 1, -1) : $this->default;
|
||||||
|
|
||||||
|
if ($default === "NULL") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Checking if another pairs of brackets surrounds the value
|
||||||
|
if ($this->isBetween($default, '(', ')')) {
|
||||||
|
$default = substr($default, 1, -1);
|
||||||
|
|
||||||
|
if (is_numeric($default)) {
|
||||||
|
return (int) $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($this->isBetween($default, "'", "'")) {
|
||||||
|
$default = new class($default) implements \Stringable {
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $default
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return substr($this->default, 1, -1);;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$default = new class($default) implements \Stringable {
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $default
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->default;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string) $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isBetween(string $str, string $first, string $last) : bool
|
||||||
|
{
|
||||||
|
return str_starts_with($str, $first) && str_ends_with($str, $last);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
src/Entity/Mssql/Table.php
Normal file
17
src/Entity/Mssql/Table.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Ulmus\Entity\Mssql;
|
||||||
|
|
||||||
|
use Ulmus\EntityCollection,
|
||||||
|
Ulmus\Entity\Field\Datetime;
|
||||||
|
|
||||||
|
use Ulmus\{Attribute\Obj\Table as TableObj,};
|
||||||
|
use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual, Where};
|
||||||
|
|
||||||
|
#[TableObj(name: "tables", database: "information_schema")]
|
||||||
|
class Table extends \Ulmus\Entity\InformationSchema\Table
|
||||||
|
{
|
||||||
|
#[Relation(type: "oneToMany", key: "name", foreignKey: [ Column::class, 'tableName' ], entity: Column::class)]
|
||||||
|
#[Where('TABLE_SCHEMA', generateValue: [ Table::class, 'getSchema' ])]
|
||||||
|
public EntityCollection $columns;
|
||||||
|
}
|
@ -34,6 +34,9 @@ trait EntityTrait {
|
|||||||
#[Ignore]
|
#[Ignore]
|
||||||
protected DatasetHandler $datasetHandler;
|
protected DatasetHandler $datasetHandler;
|
||||||
|
|
||||||
|
#[Ignore]
|
||||||
|
public string $loadedFromAdapter;
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function __construct(iterable|null $dataset = null)
|
public function __construct(iterable|null $dataset = null)
|
||||||
{
|
{
|
||||||
@ -168,7 +171,7 @@ trait EntityTrait {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Ignore]
|
#[Ignore]
|
||||||
public function __get(string $name)
|
public function __get(string $name) : mixed
|
||||||
{
|
{
|
||||||
$relation = new Repository\RelationBuilder($this);
|
$relation = new Repository\RelationBuilder($this);
|
||||||
|
|
||||||
|
@ -32,21 +32,11 @@ trait SqlMigrationTrait
|
|||||||
|
|
||||||
public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
|
public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
|
||||||
{
|
{
|
||||||
if ($field['action'] === 'add') {
|
|
||||||
if (! empty($field['previous'])) {
|
|
||||||
$position = sprintf('AFTER %s', $this->escapeIdentifier($field['previous']->field, AdapterInterface::IDENTIFIER_FIELD));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$position = "FIRST";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(" ", array_filter([
|
return implode(" ", array_filter([
|
||||||
strtoupper($field['action']),
|
strtoupper($field['action']),
|
||||||
$this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD),
|
$this->escapeIdentifier($definition->getSqlName(), AdapterInterface::IDENTIFIER_FIELD),
|
||||||
$definition->getSqlType(),
|
$definition->getSqlType(),
|
||||||
$definition->getSqlParams(),
|
$definition->getSqlParams(),
|
||||||
$position ?? null,
|
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,19 +438,19 @@ class Repository implements RepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( $attribute ) {
|
if ( $attribute ) {
|
||||||
$alias = $attribute->alias ?? $item;
|
$attribute->alias ??= $item;
|
||||||
|
|
||||||
$entity = $attribute->entity ?? $this->entityResolver->getPropertyEntityType($item);
|
$attribute->entity ??= $this->entityResolver->getPropertyEntityType($item);
|
||||||
|
|
||||||
foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
foreach($attribute->entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) {
|
||||||
if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) {
|
if ( null === $attribute->entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) {
|
||||||
$escAlias = $this->escapeIdentifier($alias);
|
$escAlias = $this->escapeIdentifier($attribute->alias);
|
||||||
$fieldName = $this->escapeIdentifier($key);
|
$fieldName = $this->escapeIdentifier($key);
|
||||||
|
|
||||||
$name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
$name = $attribute->entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name;
|
||||||
|
|
||||||
if ($canSelect) {
|
if ($canSelect) {
|
||||||
$this->select("$escAlias.$fieldName as $alias\${$name}");
|
$this->select("$escAlias.$fieldName as {$attribute->alias}\${$name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -459,15 +459,15 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipWhere, $options)) {
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ] ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ] ) as $condition) {
|
||||||
if ( is_object($condition->field) && ( $condition->field->entityClass !== $entity ) ) {
|
if ( is_object($condition->field) && ( $condition->field->entityClass !== $attribute->entity ) ) {
|
||||||
$this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$this->where(is_object($condition->field) ? $condition->field : $attribute->entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipHaving, $options)) {
|
||||||
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) {
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) {
|
||||||
$this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$this->having(is_object($condition->field) ? $condition->field : $attribute->entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -480,9 +480,9 @@ class Repository implements RepositoryInterface
|
|||||||
$this->close();
|
$this->close();
|
||||||
|
|
||||||
$key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key;
|
$key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key;
|
||||||
$foreignKey = $this->evalClosure($attribute->foreignKey, $entity, $alias);
|
$foreignKey = $this->evalClosure($attribute->foreignKey, $attribute->entity, $attribute->alias);
|
||||||
|
|
||||||
$this->join($isRelation ? "LEFT" : $attribute->type, $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) {
|
$this->join($isRelation ? "LEFT" : $attribute->type, $attribute->entity::resolveEntity()->tableName(), $key, $attribute->foreignKey($this), $attribute->alias, function($join) use ($item, $attribute, $options) {
|
||||||
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) {
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) {
|
||||||
if ( ! is_object($condition->field) ) {
|
if ( ! is_object($condition->field) ) {
|
||||||
@ -493,10 +493,10 @@ class Repository implements RepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Adding directly
|
# Adding directly
|
||||||
if ( $field->entityClass === $entity ) {
|
if ( $field->entityClass === $attribute->entity ) {
|
||||||
$field->alias = $alias;
|
$field->alias = $attribute->alias;
|
||||||
|
|
||||||
$join->where(is_object($field) ? $field : $entity::field($field, $alias), $condition->getValue(), $condition->operator);
|
$join->where(is_object($field) ? $field : $attribute->entity::field($field, $attribute->alias), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,7 +536,7 @@ class Repository implements RepositoryInterface
|
|||||||
# Apply FILTER annotation to this too !
|
# Apply FILTER annotation to this too !
|
||||||
foreach(array_filter((array) $fields) as $item) {
|
foreach(array_filter((array) $fields) as $item) {
|
||||||
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]) ) {
|
if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]) ) {
|
||||||
$alias = $relation->alias ?? $item;
|
$relation->alias ??= $item;
|
||||||
|
|
||||||
if ( $relation->isManyToMany() ) {
|
if ( $relation->isManyToMany() ) {
|
||||||
$entity = $relation->bridge;
|
$entity = $relation->bridge;
|
||||||
@ -545,13 +545,14 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
extract(Repository\RelationBuilder::relationAnnotations($item, $relation));
|
extract(Repository\RelationBuilder::relationAnnotations($item, $relation));
|
||||||
|
|
||||||
$repository->join(Query\Join::TYPE_INNER, $bridgeEntity->tableName(), $relation->bridge::field($relationRelation->key, $relation->bridgeField), $relationRelation->entity::field($relationRelation->foreignKey, $alias), $relation->bridgeField)
|
$repository->join(Query\Join::TYPE_INNER, $bridgeEntity->tableName(), $relation->bridge::field($relationRelation->key, $relation->bridgeField), $relationRelation->entity::field($relationRelation->foreignKey, $relation->alias), $relation->bridgeField)
|
||||||
->where( $entity::field($bridgeRelation->key, $relation->bridgeField), $entity::field($bridgeRelation->foreignKey, $this->alias))
|
->where( $entity::field($bridgeRelation->key, $relation->bridgeField), $entity::field($bridgeRelation->foreignKey, $this->alias))
|
||||||
->selectJsonEntity($relationRelation->entity, $alias)->open();
|
->selectJsonEntity($relationRelation->entity, $relation->alias)->open();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$entity = $relation->entity ?? $this->entityResolver->getPropertyEntityType($name);
|
$relation->entity ??= $this->entityResolver->getPropertyEntityType($name);
|
||||||
$repository = $entity::repository()->selectJsonEntity($entity, $alias)->open();
|
$entity = $relation->entity;
|
||||||
|
$repository = $relation->entity::repository()->selectJsonEntity($entity, $relation->alias)->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
# $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true);
|
# $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true);
|
||||||
@ -560,16 +561,21 @@ class Repository implements RepositoryInterface
|
|||||||
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ] ) as $condition) {
|
foreach($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ] ) as $condition) {
|
||||||
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
$repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Filter::class ]) as $filter) {
|
||||||
|
call_user_func_array([$this->entityClass, $filter->method], [$this, $item, true]);
|
||||||
|
}
|
||||||
|
|
||||||
$repository->close();
|
$repository->close();
|
||||||
|
|
||||||
$key = is_string($relation->key) ? $this->entityClass::field($relation->key) : $relation->key;
|
$key = is_string($relation->key) ? $this->entityClass::field($relation->key) : $relation->key;
|
||||||
|
|
||||||
if (! $relation->isManyToMany() ) {
|
if (! $relation->isManyToMany() ) {
|
||||||
$foreignKey = is_string($relation->foreignKey) ? $entity::field($relation->foreignKey, $alias) : $relation->foreignKey;
|
$foreignKey = is_string($relation->foreignKey) ? $entity::field($relation->foreignKey, $relation->alias) : $relation->foreignKey;
|
||||||
|
|
||||||
$repository->where( $foreignKey, $key);
|
$repository->where( $foreignKey, $key);
|
||||||
}
|
}
|
||||||
@ -590,6 +596,7 @@ class Repository implements RepositoryInterface
|
|||||||
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] ))) {
|
if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] ))) {
|
||||||
$order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]);
|
$order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]);
|
||||||
$where = $this->entityResolver->searchFieldAnnotationList($name, [ Where::class ]);
|
$where = $this->entityResolver->searchFieldAnnotationList($name, [ Where::class ]);
|
||||||
|
$filters = $this->entityResolver->searchFieldAnnotationList($name, [ Filter::class ]);
|
||||||
|
|
||||||
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->getPropertyEntityType($name);
|
$baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->getPropertyEntityType($name);
|
||||||
$baseEntityResolver = $baseEntity::resolveEntity();
|
$baseEntityResolver = $baseEntity::resolveEntity();
|
||||||
@ -610,6 +617,10 @@ class Repository implements RepositoryInterface
|
|||||||
$repository->where($condition->field, $condition->getValue(/* why repository sent here ??? $this */), $condition->operator, $condition->condition);
|
$repository->where($condition->field, $condition->getValue(/* why repository sent here ??? $this */), $condition->operator, $condition->condition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($filters as $filter) {
|
||||||
|
call_user_func_array([ $this->entityClass, $filter->method ], [ $repository, $name, true ]);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($order as $item) {
|
foreach ($order as $item) {
|
||||||
$repository->orderBy($item->field, $item->order);
|
$repository->orderBy($item->field, $item->order);
|
||||||
}
|
}
|
||||||
@ -628,7 +639,8 @@ class Repository implements RepositoryInterface
|
|||||||
|
|
||||||
$repository->where($key, $values);
|
$repository->where($key, $values);
|
||||||
|
|
||||||
$results = $repository->loadAll();
|
$loadMethod = $relation->isOneToOne() ? 'loadAll' : $relation->function();
|
||||||
|
$results = $repository->{$loadMethod}();
|
||||||
|
|
||||||
if ($relation->isOneToOne()) {
|
if ($relation->isOneToOne()) {
|
||||||
foreach ($collection as $item) {
|
foreach ($collection as $item) {
|
||||||
@ -638,7 +650,8 @@ class Repository implements RepositoryInterface
|
|||||||
elseif ($relation->isOneToMany()) {
|
elseif ($relation->isOneToMany()) {
|
||||||
foreach ($collection as $item) {
|
foreach ($collection as $item) {
|
||||||
$item->$name = $baseEntity::entityCollection();
|
$item->$name = $baseEntity::entityCollection();
|
||||||
$item->$name->mergeWith($results->searchAll($item->$entityProperty, $property));
|
$search = $results->searchAll($item->$entityProperty, $property);
|
||||||
|
$item->$name->mergeWith($search);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -646,7 +659,7 @@ class Repository implements RepositoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
## AWAITING THE Closures in constant expression from PHP 8.5 !
|
## AWAITING THE Closures in constant expression from PHP 8.5 !
|
||||||
protected function evalClosure(\Stringable|string $content, string $entityClass, mixed $alias = self::DEFAULT_ALIAS) : mixed
|
protected function evalClosure(mixed $content, string $entityClass, mixed $alias = self::DEFAULT_ALIAS) : mixed
|
||||||
{
|
{
|
||||||
if (is_string($content)) {
|
if (is_string($content)) {
|
||||||
if ( str_starts_with($content, 'fn(') ) {
|
if ( str_starts_with($content, 'fn(') ) {
|
||||||
|
@ -2,19 +2,35 @@
|
|||||||
|
|
||||||
namespace Ulmus\Repository;
|
namespace Ulmus\Repository;
|
||||||
|
|
||||||
use Ulmus\{Repository, Query, Ulmus, Common};
|
use Ulmus\{EntityCollection, Repository, Query, Entity, Ulmus, Common};
|
||||||
|
|
||||||
class MssqlRepository extends Repository {
|
class MssqlRepository extends Repository {
|
||||||
|
|
||||||
protected function finalizeQuery() : void
|
protected function finalizeQuery() : void
|
||||||
{
|
{
|
||||||
if ( $this->queryBuilder->getFragment(Query\MsSQL\Offset::class) ) {
|
$delete = $this->queryBuilder->getFragment(Query\Delete::class);
|
||||||
if (null === $order = $this->queryBuilder->getFragment(Query\OrderBy::class)) {
|
$offset = $this->queryBuilder->getFragment(Query\MsSQL\Offset::class);
|
||||||
|
|
||||||
|
if ($offset) {
|
||||||
|
if ( $delete ) {
|
||||||
|
$delete->top = $offset->limit;
|
||||||
|
|
||||||
|
$this->queryBuilder->removeFragment(Query\MsSQL\Offset::class);
|
||||||
|
}
|
||||||
|
elseif (null === $this->queryBuilder->getFragment(Query\OrderBy::class)) {
|
||||||
$this->orderBy("(SELECT 0)");
|
$this->orderBy("(SELECT 0)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function listColumns(? string $table = null) : EntityCollection
|
||||||
|
{
|
||||||
|
$table ??= $this->entityResolver->tableName();
|
||||||
|
$this->showColumnsSqlQuery($table);
|
||||||
|
|
||||||
|
return $this->collectionFromQuery(Entity\Mssql\Column::class)->iterate(fn($e) => $e->tableName = $table);
|
||||||
|
}
|
||||||
|
|
||||||
protected function serverRequestCountRepository() : Repository
|
protected function serverRequestCountRepository() : Repository
|
||||||
{
|
{
|
||||||
return new static($this->entityClass, $this->alias, $this->adapter);
|
return new static($this->entityClass, $this->alias, $this->adapter);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ulmus\Repository;
|
namespace Ulmus\Repository;
|
||||||
|
|
||||||
use Ulmus\{ Repository, Query, Ulmus};
|
use Ulmus\{EntityCollection, Repository, Query, Entity, Ulmus};
|
||||||
|
|
||||||
class MysqlRepository extends Repository {
|
class MysqlRepository extends Repository {
|
||||||
use JsonConditionTrait;
|
use JsonConditionTrait;
|
||||||
@ -14,6 +14,14 @@ class MysqlRepository extends Repository {
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function listColumns(? string $table = null) : EntityCollection
|
||||||
|
{
|
||||||
|
$table ??= $this->entityResolver->tableName();
|
||||||
|
$this->showColumnsSqlQuery($table);
|
||||||
|
|
||||||
|
return $this->collectionFromQuery(Entity\Mysql\Column::class)->iterate(fn($e) => $e->tableName = $table);
|
||||||
|
}
|
||||||
|
|
||||||
public function createSqlQuery() : self
|
public function createSqlQuery() : self
|
||||||
{
|
{
|
||||||
if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) {
|
if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) {
|
||||||
|
@ -262,9 +262,9 @@ class RelationBuilder
|
|||||||
|
|
||||||
public function oneToMany(string $name, Relation $relation) : Repository
|
public function oneToMany(string $name, Relation $relation) : Repository
|
||||||
{
|
{
|
||||||
$baseEntity = $relation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
$relation->entity ??= $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type;
|
||||||
|
|
||||||
$this->repository = $baseEntity::repository();
|
$this->repository = $relation->entity::repository();
|
||||||
|
|
||||||
$this->applyWhere();
|
$this->applyWhere();
|
||||||
|
|
||||||
@ -281,7 +281,8 @@ class RelationBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($value)) {
|
if (isset($value)) {
|
||||||
$this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), $value );
|
#$this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $relation->entity::field($relation->foreignKey), $value );
|
||||||
|
$this->repository->where( $relation->foreignKey($this->repository), $value );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$this->repository->where(Sql::raw('TRUE'), Sql::raw('FALSE'));
|
$this->repository->where(Sql::raw('TRUE'), Sql::raw('FALSE'));
|
||||||
|
@ -27,7 +27,6 @@ class SqliteRepository extends Repository {
|
|||||||
return $this->collectionFromQuery(Entity\Sqlite\Column::class)->iterate(fn($e) => $e->tableName = $table);
|
return $this->collectionFromQuery(Entity\Sqlite\Column::class)->iterate(fn($e) => $e->tableName = $table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function serverRequestCountRepository() : Repository
|
protected function serverRequestCountRepository() : Repository
|
||||||
{
|
{
|
||||||
return new static($this->entityClass, $this->alias, $this->adapter);
|
return new static($this->entityClass, $this->alias, $this->adapter);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user