- WIP on migration and some bug fixes linked to withJoin() select problems
This commit is contained in:
		
							parent
							
								
									761e0401b0
								
							
						
					
					
						commit
						cc6048d4ee
					
				| @ -4,7 +4,7 @@ namespace Ulmus\Adapter; | ||||
| 
 | ||||
| use Ulmus\{ConnectionAdapter, Entity\InformationSchema\Table, Migration\FieldDefinition, Repository, QueryBuilder\Sql\MysqlQueryBuilder, Entity}; | ||||
| 
 | ||||
| trait DefaultAdapterTrait | ||||
| UNUSED? trait DefaultAdapterTrait | ||||
| { | ||||
|     public function repositoryClass() : string | ||||
|     { | ||||
|  | ||||
| @ -9,10 +9,14 @@ use Ulmus\Exception\AdapterConfigurationException; | ||||
| use Ulmus\Ulmus; | ||||
| 
 | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| use Ulmus\{Entity\InformationSchema\Table, Migration\MigrateInterface, Repository, QueryBuilder}; | ||||
| use Ulmus\{Entity\InformationSchema\Table, | ||||
|     Migration\MigrateInterface, | ||||
|     Migration\SqlMigrationTrait, | ||||
|     Repository, | ||||
|     QueryBuilder}; | ||||
| 
 | ||||
| class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     use SqlAdapterTrait; | ||||
|     use SqlAdapterTrait, SqlMigrationTrait; | ||||
| 
 | ||||
|     const ALLOWED_ATTRIBUTES = [ | ||||
|         'default', 'primary_key', 'auto_increment', | ||||
| @ -205,7 +209,14 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|             $this->traceOn = $configuration['trace_on']; | ||||
|         } | ||||
|     } | ||||
|       | ||||
| 
 | ||||
|     public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string | ||||
|     { | ||||
|         $mapper = new MsSQLFieldMapper($field); | ||||
| 
 | ||||
|         return $typeOnly ? $mapper->type : $mapper->render(); | ||||
|     } | ||||
| 
 | ||||
|     public static function escapeIdentifier(string $segment, int $type) : string  | ||||
|     { | ||||
|         switch($type) { | ||||
|  | ||||
							
								
								
									
										29
									
								
								src/Adapter/MsSQLFieldMapper.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/Adapter/MsSQLFieldMapper.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\Adapter; | ||||
| 
 | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| 
 | ||||
| class MsSQLFieldMapper extends SqlFieldMapper | ||||
| { | ||||
|     public function map() : void | ||||
|     { | ||||
|         parent::map(); | ||||
| 
 | ||||
|         if (in_array($this->type, [ 'CHAR', 'VARCHAR', 'TEXT', ])) { | ||||
|             $this->type = "N" . $this->type; | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     /* @TODO ! | ||||
|     public function postProcess() : void | ||||
|     { | ||||
|         if ( | ||||
|             in_array($this->type, [ 'BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB', 'JSON', 'TEXT', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'GEOMETRY' ]) && | ||||
|             ! is_object($this->field->default ?? false) # Could be a functional default, which would now be valid
 | ||||
|         ) { | ||||
|             unset($this->field->default); | ||||
|         } | ||||
|     } | ||||
|      * */ | ||||
| } | ||||
| @ -6,6 +6,7 @@ use Ulmus\ConnectionAdapter; | ||||
| use Ulmus\Entity\Mysql\Table; | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| use Ulmus\Migration\MigrateInterface; | ||||
| use Ulmus\Migration\SqlMigrationTrait; | ||||
| use Ulmus\QueryBuilder\Sql; | ||||
| use Ulmus\Common\PdoObject; | ||||
| 
 | ||||
| @ -13,7 +14,7 @@ use Ulmus\Exception\AdapterConfigurationException; | ||||
| use Ulmus\Repository; | ||||
| 
 | ||||
| class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     use SqlAdapterTrait; | ||||
|     use SqlAdapterTrait, SqlMigrationTrait; | ||||
| 
 | ||||
|     const ALLOWED_ATTRIBUTES = [ | ||||
|         'default', 'primary_key', 'auto_increment', 'update', | ||||
|  | ||||
| @ -6,10 +6,6 @@ use Ulmus\Migration\FieldDefinition; | ||||
| 
 | ||||
| class MySQLFieldMapper extends SqlFieldMapper | ||||
| { | ||||
|     public readonly string $type; | ||||
| 
 | ||||
|     public readonly string $length; | ||||
| 
 | ||||
|     public function map() : void | ||||
|     { | ||||
|         $type = $this->field->type; | ||||
|  | ||||
| @ -7,10 +7,10 @@ use Ulmus\Common\PdoObject; | ||||
| use Ulmus\ConnectionAdapter; | ||||
| use Ulmus\Entity; | ||||
| use Ulmus\Migration\FieldDefinition; | ||||
| use Ulmus\{Migration\MigrateInterface, Repository, QueryBuilder}; | ||||
| use Ulmus\{Migration\MigrateInterface, Migration\SqlMigrationTrait, Repository, QueryBuilder}; | ||||
| 
 | ||||
| class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface { | ||||
|     use SqlAdapterTrait; | ||||
|     use SqlAdapterTrait, SqlMigrationTrait; | ||||
| 
 | ||||
|     const ALLOWED_ATTRIBUTES = [ | ||||
|         'default', 'primary_key', 'auto_increment', 'collate nocase', 'collate binary', 'collate rtrim', | ||||
|  | ||||
| @ -36,44 +36,11 @@ trait SqlAdapterTrait | ||||
|         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 | ||||
|     { | ||||
|         $mapper = new SqlFieldMapper($field); | ||||
| 
 | ||||
|         return $typeOnly ? $mapper->type : $mapper->render(); | ||||
|     } | ||||
| 
 | ||||
|     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 (! empty($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, | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
|     public function writableValue(mixed $value) : mixed | ||||
|     { | ||||
|         switch (true) { | ||||
| @ -92,9 +59,4 @@ trait SqlAdapterTrait | ||||
| 
 | ||||
|         return $value; | ||||
|     } | ||||
|      | ||||
|     public function splitAlterQuery() : bool | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,9 +7,9 @@ use Ulmus\Entity; | ||||
| 
 | ||||
| class SqlFieldMapper | ||||
| { | ||||
|     public readonly string $type; | ||||
|     public string $type; | ||||
| 
 | ||||
|     public readonly string $length; | ||||
|     public string $length; | ||||
| 
 | ||||
|     public function __construct( | ||||
|         public FieldDefinition $field, | ||||
|  | ||||
| @ -10,6 +10,12 @@ class Attribute | ||||
|     public static function handleArrayField(null|\Stringable|string|array $field, null|string|bool $alias = Repository::DEFAULT_ALIAS, string $separator = ', ') : mixed | ||||
|     { | ||||
|         if ( is_array($field) ) { | ||||
|             if (count($field) < 2) { | ||||
|                 throw new \RuntimeException( | ||||
|                     sprintf("Array field must be formed of at least two things, a class name, and a property. (received %s)", json_encode($field)) | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             $class = array_shift($field); | ||||
|             $field[1] ??= $alias; | ||||
| 
 | ||||
|  | ||||
| @ -10,6 +10,7 @@ class Virtual extends Field implements ResettablePropertyInterface { | ||||
|     public function __construct( | ||||
|         public null|string|array $method = null, | ||||
|         public ? \Closure $closure = null, | ||||
|         public null|string $name = null, | ||||
|     ) { | ||||
|         $this->method ??= [ static::class, 'noop' ]; | ||||
|     } | ||||
|  | ||||
| @ -114,7 +114,12 @@ class EntityResolver { | ||||
|             throw new \InvalidArgumentException("Can't find entity relation's column named `$name` from entity {$this->entityClass}"); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     public function getPropertyEntityType(string $name) : false|string | ||||
|     { | ||||
|         return $this->reflectedClass->getProperties(true)[$name]->getTypes()[0]->type ?? false; | ||||
|     } | ||||
| 
 | ||||
|     public function searchFieldAnnotation(string $field, array|object|string $annotationType, bool $caseSensitive = true) : ? object | ||||
|     { | ||||
|         return $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive)[0] ?? null; | ||||
|  | ||||
| @ -56,7 +56,10 @@ class DatasetHandler | ||||
|             } | ||||
|             elseif ( $field->expectType('array') ) { | ||||
|                 if ( is_string($value)) { | ||||
|                     if (substr($value, 0, 1) === "a") { | ||||
|                     if (empty($value)) { | ||||
|                         yield $field->name => []; | ||||
|                     } | ||||
|                     elseif (substr($value, 0, 1) === "a") { | ||||
| 
 | ||||
|                         yield $field->name => unserialize($value); | ||||
|                     } | ||||
|  | ||||
| @ -13,9 +13,6 @@ class Column | ||||
| { | ||||
|     use \Ulmus\EntityTrait; | ||||
| 
 | ||||
|     #[Field\Id]
 | ||||
|     public ? id $srs_id; | ||||
| 
 | ||||
|     #[Field(name: "TABLE_CATALOG", length: 512)]
 | ||||
|     public string $tableCatalog; | ||||
| 
 | ||||
| @ -64,24 +61,10 @@ class Column | ||||
| #    #[Field(name: "COLLATION_TYPE", type: "longtext")]
 | ||||
| #    public string $type;
 | ||||
| 
 | ||||
|     #[Field(name: "COLUMN_KEY", length: 3)]
 | ||||
|     public string $key; | ||||
| 
 | ||||
|     #[Field(name: "EXTRA", length: 30)]
 | ||||
|     public string $extra; | ||||
| 
 | ||||
|     #[Field(name: "PRIVILEGES", length: 80)]
 | ||||
|     public string $privileges; | ||||
| 
 | ||||
|     #[Field(name: "COLUMN_COMMENT", length: 1024)]
 | ||||
|     public string $comment; | ||||
| 
 | ||||
| #    #[Field(name: "IS_GENERATED", length: 6)]
 | ||||
| #    public string $generated;
 | ||||
| 
 | ||||
|     #[Field(name: "GENERATION_EXPRESSION", type: "longtext")]
 | ||||
|     public ? string $generationExpression; | ||||
| 
 | ||||
|     public function matchFieldDefinition(ReflectedProperty $definition) : bool | ||||
|     { | ||||
|         $nullable = $this->nullable === 'YES'; | ||||
|  | ||||
| @ -2,8 +2,28 @@ | ||||
| 
 | ||||
| namespace Ulmus\Entity\Mysql; | ||||
| 
 | ||||
| use Ulmus\Attribute\Property\Field; | ||||
| 
 | ||||
| class Column extends \Ulmus\Entity\InformationSchema\Column | ||||
| { | ||||
|     #[Field\Id]
 | ||||
|     public ? id $srs_id; | ||||
| 
 | ||||
|     #[Field(name: "COLUMN_KEY", length: 3)]
 | ||||
|     public string $key; | ||||
| 
 | ||||
|     #[Field(name: "EXTRA", length: 30)]
 | ||||
|     public string $extra; | ||||
| 
 | ||||
|     #[Field(name: "PRIVILEGES", length: 80)]
 | ||||
|     public string $privileges; | ||||
| 
 | ||||
|     #[Field(name: "COLUMN_COMMENT", length: 1024)]
 | ||||
|     public string $comment; | ||||
| 
 | ||||
|     #[Field(name: "GENERATION_EXPRESSION", type: "longtext")]
 | ||||
|     public ? string $generationExpression; | ||||
| 
 | ||||
|     # TODO ! Handle FUNCTIONAL default value
 | ||||
|     protected function canHaveDefaultValue(): bool | ||||
|     { | ||||
| @ -12,4 +32,5 @@ class Column extends \Ulmus\Entity\InformationSchema\Column | ||||
|         ]); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| @ -5,4 +5,4 @@ namespace Ulmus\Migration\Sql; | ||||
| class SqliteMigration | ||||
| { | ||||
| 
 | ||||
| } | ||||
| }  | ||||
							
								
								
									
										57
									
								
								src/Migration/SqlMigrationTrait.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Migration/SqlMigrationTrait.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| <?php | ||||
| 
 | ||||
| namespace Ulmus\Migration; | ||||
| 
 | ||||
| use Ulmus\Adapter\AdapterInterface; | ||||
| use Ulmus\Adapter\SqlFieldMapper; | ||||
| use Ulmus\ConnectionAdapter; | ||||
| use Ulmus\Entity\Mysql\Table; | ||||
| use Ulmus\Repository; | ||||
| 
 | ||||
| trait SqlMigrationTrait | ||||
| { | ||||
|     protected array $QUERY_REPLACE = [ | ||||
|         'change' => "alter", | ||||
|     ]; | ||||
| 
 | ||||
|     public function schemaTable(ConnectionAdapter $adapter, $databaseName, string $tableName) : null|object | ||||
|     { | ||||
|         return \Ulmus\Entity\InformationSchema\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); | ||||
|     } | ||||
| 
 | ||||
|     public function mapFieldType(FieldDefinition $field, bool $typeOnly = false) : string | ||||
|     { | ||||
|         $mapper = new SqlFieldMapper($field); | ||||
| 
 | ||||
|         return $typeOnly ? $mapper->type : $mapper->render(); | ||||
|     } | ||||
| 
 | ||||
|     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, | ||||
|         ])); | ||||
|     } | ||||
| 
 | ||||
|     public function splitAlterQuery() : bool | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @ -12,6 +12,8 @@ class Select extends Fragment { | ||||
| 
 | ||||
|     public bool $union = false; | ||||
| 
 | ||||
|     public bool $isInternalSelect = false; | ||||
| 
 | ||||
|     public ? int $top = null; | ||||
| 
 | ||||
|     protected array $fields = []; | ||||
|  | ||||
| @ -416,7 +416,8 @@ class Repository implements RepositoryInterface | ||||
| 
 | ||||
|     public function withJoin(string|array $fields, array $options = []) : self | ||||
|     { | ||||
|         $canSelect = null === $this->queryBuilder->getFragment(Query\Select::class); | ||||
|         $selectObj = $this->queryBuilder->getFragment(Query\Select::class); | ||||
|         $canSelect = empty($selectObj) || $selectObj->isInternalSelect === true; | ||||
| 
 | ||||
|         if ( $canSelect ) { | ||||
|             $select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true); | ||||
| @ -424,18 +425,13 @@ class Repository implements RepositoryInterface | ||||
|         } | ||||
| 
 | ||||
|         # @TODO Apply FILTER annotation to this too !
 | ||||
|         foreach(array_filter((array) $fields) as $item) { | ||||
|             if ( isset($this->joined[$item]) ) { | ||||
|                 continue; | ||||
|             } | ||||
|             else { | ||||
|                 $this->joined[$item] = true; | ||||
|             } | ||||
|         foreach(array_filter((array) $fields, fn($e) => ! isset($this->joined[$e])) as $item) { | ||||
|             $this->joined[$item] = true; | ||||
| 
 | ||||
|             $attribute = $this->entityResolver->searchFieldAnnotation($item, [ Join::class  ]) ?: | ||||
|                 $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]); | ||||
| 
 | ||||
|             $isRelation = ( $attribute instanceof Relation ) || ($attribute instanceof Relation); | ||||
|             $isRelation = $attribute instanceof Relation; | ||||
| 
 | ||||
|             if ($isRelation && ( $attribute->isManyToMany() )) { | ||||
|                 throw new \Exception("Many-to-many relation can not be preloaded within joins."); | ||||
| @ -444,7 +440,7 @@ class Repository implements RepositoryInterface | ||||
|             if ( $attribute ) { | ||||
|                 $alias = $attribute->alias ?? $item; | ||||
| 
 | ||||
|                 $entity = $attribute->entity ?? $this->entityResolver->reflectedClass->getProperties(true)[$item]->getTypes()[0]->type; | ||||
|                 $entity = $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 ]) ) { | ||||
| @ -462,12 +458,12 @@ class Repository implements RepositoryInterface | ||||
|                 $this->open(); | ||||
| 
 | ||||
|                 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); | ||||
|                     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 ( ! in_array(WithOptionEnum::SkipHaving, $options)) { | ||||
|                     foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) { | ||||
| @ -485,9 +481,9 @@ class Repository implements RepositoryInterface | ||||
| 
 | ||||
|                 $key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key; | ||||
| 
 | ||||
|                 $foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey; | ||||
|                 $foreignKey = $this->evalClosure($attribute->foreignKey, $entity, $alias); | ||||
| 
 | ||||
|                 $this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) { | ||||
|                 $this->join($isRelation ? "LEFT" : $attribute->type, $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) { | ||||
|                     if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) { | ||||
|                         foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) { | ||||
|                             if ( ! is_object($condition->field) ) { | ||||
| @ -518,6 +514,12 @@ class Repository implements RepositoryInterface | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if ($canSelect) { | ||||
|             if ( $selectObj ??= $this->queryBuilder->getFragment(Query\Select::class) ) { | ||||
|                 $selectObj->isInternalSelect = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
| @ -549,7 +551,7 @@ class Repository implements RepositoryInterface | ||||
|                         ->selectJsonEntity($relationRelation->entity, $alias)->open(); | ||||
|                 } | ||||
|                 else { | ||||
|                     $entity = $relation->entity ?? $this->entityResolver->properties[$item]['type']; | ||||
|                     $entity = $relation->entity ?? $this->entityResolver->getPropertyEntityType($name); | ||||
|                     $repository = $entity::repository()->selectJsonEntity($entity, $alias)->open(); | ||||
|                 } | ||||
| 
 | ||||
| @ -590,7 +592,7 @@ class Repository implements RepositoryInterface | ||||
|                 $order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]); | ||||
|                 $where = $this->entityResolver->searchFieldAnnotationList($name, [ Where::class ]); | ||||
| 
 | ||||
|                 $baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type']; | ||||
|                 $baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->getPropertyEntityType($name); | ||||
|                 $baseEntityResolver = $baseEntity::resolveEntity(); | ||||
| 
 | ||||
|                 $property = ($baseEntityResolver->field($relation->foreignKey, 01, false) ?: $baseEntityResolver->field($relation->foreignKey, 02))['name']; | ||||
| @ -644,6 +646,22 @@ class Repository implements RepositoryInterface | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ## AWAITING THE Closures in constant expression from PHP 8.5 !
 | ||||
|     protected function evalClosure(string $content, string $entityClass, mixed $alias = self::DEFAULT_ALIAS) : mixed | ||||
|     { | ||||
|         if (is_string($content)) { | ||||
|             if ( str_starts_with($content, 'fn(') ) { | ||||
|                 $closure = eval("return $content;"); | ||||
| 
 | ||||
|                 return $closure($this); | ||||
|             } | ||||
| 
 | ||||
|             return $entityClass::field($content, $alias); | ||||
|         } | ||||
| 
 | ||||
|         return $content; | ||||
|     } | ||||
| 
 | ||||
|     public function filterServerRequest(SearchRequest\SearchRequestInterface $searchRequest, bool $count = true) : self | ||||
|     { | ||||
|         if ($count) { | ||||
| @ -748,7 +766,15 @@ class Repository implements RepositoryInterface | ||||
| 
 | ||||
|     public function instanciateEntity(? string $entityClass = null) : object | ||||
|     { | ||||
|         $entity = ( new \ReflectionClass($entityClass ?? $this->entityClass) )->newInstanceWithoutConstructor(); | ||||
|         $entityClass ??= $this->entityClass; | ||||
| 
 | ||||
|         try { | ||||
|             $entity = new $entityClass(); | ||||
|         } | ||||
|         catch (\Throwable $ex) { | ||||
|             $entity = ( new \ReflectionClass($entityClass) )->newInstanceWithoutConstructor(); | ||||
|         } | ||||
| 
 | ||||
|         $entity->initializeEntity(); | ||||
| 
 | ||||
|         return $entity; | ||||
|  | ||||
| @ -57,7 +57,7 @@ class RelationBuilder | ||||
| 
 | ||||
|         } | ||||
|         else { | ||||
|             if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class ] ) ) { | ||||
|             if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class, Join::class ] ) ) { | ||||
|                 return $this->instanciateEmptyObject($name, $relation); | ||||
|             } | ||||
|             elseif ($virtual = $this->resolveVirtual($name)) { | ||||
| @ -91,7 +91,7 @@ class RelationBuilder | ||||
| 
 | ||||
|     protected function resolveRelation(string $name) : mixed | ||||
|     { | ||||
|         if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class ] ) ) ) { | ||||
|         if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Relation::class, /*Join::class*/ ] ) ) ) { | ||||
|             $this->orders = $this->resolver->searchFieldAnnotationList($name, [ OrderBy::class ] ); | ||||
|             $this->wheres = $this->resolver->searchFieldAnnotationList($name, [  Where::class ] ); | ||||
|             $this->filters = $this->resolver->searchFieldAnnotationList($name, [ Filter::class ] ); | ||||
|  | ||||
| @ -5,7 +5,7 @@ namespace Ulmus\Repository; | ||||
| enum WithOptionEnum | ||||
| { | ||||
|     case SkipWhere; | ||||
|             case SkipHaving; | ||||
|     case SkipHaving; | ||||
|     case SkipFilter; | ||||
|     case SkipOrderBy; | ||||
|     case SkipJoinWhere; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user