- 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\Migration\FieldDefinition; | ||||
| use Ulmus\{Entity\InformationSchema\Table, | ||||
| use Ulmus\{ConnectionAdapter, | ||||
|     Entity\InformationSchema\Table, | ||||
|     Migration\MigrateInterface, | ||||
|     Migration\SqlMigrationTrait, | ||||
|     Repository, | ||||
| @ -246,4 +247,13 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     { | ||||
|         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) | ||||
|             ->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 { | ||||
|     public function __construct( | ||||
|         public string $method = "" | ||||
|     ) | ||||
|     {} | ||||
|     ) {} | ||||
| } | ||||
| @ -3,6 +3,7 @@ | ||||
| namespace Ulmus\Attribute\Property; | ||||
| 
 | ||||
| use Ulmus\Attribute\Attribute; | ||||
| use Ulmus\Repository; | ||||
| 
 | ||||
| #[\Attribute]
 | ||||
| class Join implements ResettablePropertyInterface { | ||||
| @ -16,4 +17,20 @@ class Join implements ResettablePropertyInterface { | ||||
|         $this->key = Attribute::handleArrayField($this->key); | ||||
|         $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; | ||||
| 
 | ||||
| use Ulmus\Attribute\Attribute; | ||||
| use Ulmus\Repository; | ||||
| 
 | ||||
| #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||
| class Relation implements ResettablePropertyInterface { | ||||
| @ -21,6 +22,7 @@ class Relation implements ResettablePropertyInterface { | ||||
|         public null|string $entity = null, | ||||
|         public null|string $join = null, | ||||
|         public null|string $function = null, | ||||
|         public null|string $alias = null, | ||||
|     ) { | ||||
|         $this->key = Attribute::handleArrayField($this->key); | ||||
|         $this->foreignKey = Attribute::handleArrayField($this->foreignKey); | ||||
| @ -83,4 +85,20 @@ class Relation implements ResettablePropertyInterface { | ||||
|     { | ||||
|         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,15 +111,17 @@ class DatasetHandler | ||||
|         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 ] ); | ||||
| 
 | ||||
|             if ($relation) { | ||||
|                 $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() ) { | ||||
|                         # @TODO TO INCLUDED INTO getTypes() RETURNED CLASS WHEN DONE !
 | ||||
|                         yield $name => ( new \ReflectionClass($field->getTypes()[0]) )->newInstanceWithoutConstructor(); | ||||
| @ -132,9 +134,10 @@ class DatasetHandler | ||||
|                     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 ( null !== $value = $entity->__isset($name) ?? null ) { | ||||
|                     $value = $entity->$name ?? null; | ||||
| 
 | ||||
|                     if ( null !== $value ) { | ||||
|                         if ( is_iterable($value) ) { | ||||
|                             $list = []; | ||||
| 
 | ||||
|  | ||||
| @ -69,16 +69,19 @@ class Column | ||||
|     { | ||||
|         $nullable = $this->nullable === 'YES'; | ||||
| 
 | ||||
|         $definitionValue = $this->getDefinitionValue($definition); | ||||
| 
 | ||||
|         if ($nullable !== $definition->allowsNull()) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if ( isset($definition->value) && $this->canHaveDefaultValue() ) { | ||||
|             if ( $definition->value !== $this->defaultValue()) { | ||||
|         if ( isset($definitionValue) && $this->canHaveDefaultValue() ) { | ||||
|             if ( $definitionValue !== $this->defaultValue() ) { | ||||
|                 # dump($definition->value, $this->defaultValue(), $definition->name);
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         elseif (! isset($definition->value)) { | ||||
|         elseif (! isset($definitionValue)) { | ||||
|             if ( ! $this->defaultValueIsNull() ) { | ||||
|                 return false; | ||||
|             } | ||||
| @ -87,6 +90,12 @@ class Column | ||||
|         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 | ||||
|     { | ||||
|         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]
 | ||||
|     protected DatasetHandler $datasetHandler; | ||||
| 
 | ||||
|     #[Ignore]
 | ||||
|     public string $loadedFromAdapter; | ||||
| 
 | ||||
|     #[Ignore]
 | ||||
|     public function __construct(iterable|null $dataset = null) | ||||
|     { | ||||
| @ -168,7 +171,7 @@ trait EntityTrait { | ||||
|     } | ||||
| 
 | ||||
|     #[Ignore]
 | ||||
|     public function __get(string $name) | ||||
|     public function __get(string $name) : mixed | ||||
|     { | ||||
|         $relation = new Repository\RelationBuilder($this); | ||||
| 
 | ||||
|  | ||||
| @ -32,21 +32,11 @@ trait SqlMigrationTrait | ||||
| 
 | ||||
|     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, | ||||
|         ])); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -438,19 +438,19 @@ class Repository implements RepositoryInterface | ||||
|             } | ||||
| 
 | ||||
|             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) { | ||||
|                     if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) { | ||||
|                         $escAlias = $this->escapeIdentifier($alias); | ||||
|                 foreach($attribute->entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { | ||||
|                     if ( null === $attribute->entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) { | ||||
|                         $escAlias = $this->escapeIdentifier($attribute->alias); | ||||
|                         $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) { | ||||
|                             $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)) { | ||||
|                     foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ] ) as $condition) { | ||||
|                         if ( is_object($condition->field) && ( $condition->field->entityClass !== $entity ) ) { | ||||
|                             $this->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field),  $condition->getValue(), $condition->operator); | ||||
|                         if ( is_object($condition->field) && ( $condition->field->entityClass !== $attribute->entity ) ) { | ||||
|                             $this->where(is_object($condition->field) ? $condition->field : $attribute->entity::field($condition->field),  $condition->getValue(), $condition->operator); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if ( ! in_array(WithOptionEnum::SkipHaving, $options)) { | ||||
|                     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(); | ||||
| 
 | ||||
|                 $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)) { | ||||
|                         foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) { | ||||
|                             if ( ! is_object($condition->field) ) { | ||||
| @ -493,10 +493,10 @@ class Repository implements RepositoryInterface | ||||
|                             } | ||||
| 
 | ||||
|                             # Adding directly
 | ||||
|                             if ( $field->entityClass === $entity ) { | ||||
|                                 $field->alias = $alias; | ||||
|                             if ( $field->entityClass === $attribute->entity ) { | ||||
|                                 $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 !
 | ||||
|         foreach(array_filter((array) $fields) as $item) { | ||||
|             if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]) ) { | ||||
|                 $alias = $relation->alias ?? $item; | ||||
|                 $relation->alias ??= $item; | ||||
| 
 | ||||
|                 if ( $relation->isManyToMany() ) { | ||||
|                     $entity = $relation->bridge; | ||||
| @ -545,13 +545,14 @@ class Repository implements RepositoryInterface | ||||
| 
 | ||||
|                     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)) | ||||
|                         ->selectJsonEntity($relationRelation->entity, $alias)->open(); | ||||
|                         ->selectJsonEntity($relationRelation->entity, $relation->alias)->open(); | ||||
|                 } | ||||
|                 else { | ||||
|                     $entity = $relation->entity ?? $this->entityResolver->getPropertyEntityType($name); | ||||
|                     $repository = $entity::repository()->selectJsonEntity($entity, $alias)->open(); | ||||
|                     $relation->entity ??= $this->entityResolver->getPropertyEntityType($name); | ||||
|                     $entity = $relation->entity; | ||||
|                     $repository = $relation->entity::repository()->selectJsonEntity($entity, $relation->alias)->open(); | ||||
|                 } | ||||
| 
 | ||||
|                 #  $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); | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 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); | ||||
|                 } | ||||
| 
 | ||||
|                 foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Filter::class ]) as $filter) { | ||||
|                     call_user_func_array([$this->entityClass, $filter->method], [$this, $item, true]); | ||||
|                 } | ||||
| 
 | ||||
|                 $repository->close(); | ||||
| 
 | ||||
|                 $key = is_string($relation->key) ? $this->entityClass::field($relation->key) : $relation->key; | ||||
| 
 | ||||
|                 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); | ||||
|                 } | ||||
| @ -590,6 +596,7 @@ class Repository implements RepositoryInterface | ||||
|             if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] ))) { | ||||
|                 $order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]); | ||||
|                 $where = $this->entityResolver->searchFieldAnnotationList($name, [ Where::class ]); | ||||
|                 $filters = $this->entityResolver->searchFieldAnnotationList($name, [ Filter::class ]); | ||||
| 
 | ||||
|                 $baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->getPropertyEntityType($name); | ||||
|                 $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); | ||||
|                 } | ||||
| 
 | ||||
|                 foreach ($filters as $filter) { | ||||
|                     call_user_func_array([ $this->entityClass, $filter->method ], [ $repository, $name, true ]); | ||||
|                 } | ||||
| 
 | ||||
|                 foreach ($order as $item) { | ||||
|                     $repository->orderBy($item->field, $item->order); | ||||
|                 } | ||||
| @ -628,7 +639,8 @@ class Repository implements RepositoryInterface | ||||
| 
 | ||||
|                 $repository->where($key, $values); | ||||
| 
 | ||||
|                 $results = $repository->loadAll(); | ||||
|                 $loadMethod = $relation->isOneToOne() ? 'loadAll' : $relation->function(); | ||||
|                 $results = $repository->{$loadMethod}(); | ||||
| 
 | ||||
|                 if ($relation->isOneToOne()) { | ||||
|                     foreach ($collection as $item) { | ||||
| @ -638,7 +650,8 @@ class Repository implements RepositoryInterface | ||||
|                 elseif ($relation->isOneToMany()) { | ||||
|                     foreach ($collection as $item) { | ||||
|                         $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 !
 | ||||
|     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 ( str_starts_with($content, 'fn(') ) { | ||||
|  | ||||
| @ -2,19 +2,35 @@ | ||||
| 
 | ||||
| namespace Ulmus\Repository; | ||||
| 
 | ||||
| use Ulmus\{Repository, Query, Ulmus, Common}; | ||||
| use Ulmus\{EntityCollection, Repository, Query, Entity, Ulmus, Common}; | ||||
| 
 | ||||
| class MssqlRepository extends Repository { | ||||
| 
 | ||||
|     protected function finalizeQuery() : void | ||||
|     { | ||||
|         if ( $this->queryBuilder->getFragment(Query\MsSQL\Offset::class) ) { | ||||
|             if (null === $order = $this->queryBuilder->getFragment(Query\OrderBy::class)) { | ||||
|         $delete = $this->queryBuilder->getFragment(Query\Delete::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)"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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 | ||||
|     { | ||||
|         return new static($this->entityClass, $this->alias, $this->adapter); | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| namespace Ulmus\Repository; | ||||
| 
 | ||||
| use Ulmus\{  Repository, Query, Ulmus}; | ||||
| use Ulmus\{EntityCollection, Repository, Query, Entity, Ulmus}; | ||||
| 
 | ||||
| class MysqlRepository extends Repository { | ||||
|     use JsonConditionTrait; | ||||
| @ -14,6 +14,14 @@ class MysqlRepository extends Repository { | ||||
|         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 | ||||
|     { | ||||
|         if ( null === $this->queryBuilder->getFragment(Query\Engine::class) ) { | ||||
|  | ||||
| @ -262,9 +262,9 @@ class RelationBuilder | ||||
| 
 | ||||
|     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(); | ||||
| 
 | ||||
| @ -281,7 +281,8 @@ class RelationBuilder | ||||
|             } | ||||
| 
 | ||||
|             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 { | ||||
|                 $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); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     protected function serverRequestCountRepository() : Repository | ||||
|     { | ||||
|         return new static($this->entityClass, $this->alias, $this->adapter); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user