Compare commits
	
		
			2 Commits
		
	
	
		
			761e0401b0
			...
			8fff21ecb0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8fff21ecb0 | ||
| 
						 | 
					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;
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,7 @@
 | 
			
		||||
namespace Ulmus\Repository;
 | 
			
		||||
 | 
			
		||||
use Ulmus\{
 | 
			
		||||
    Ulmus, Query, Common\EntityResolver, Repository, Event,
 | 
			
		||||
    Ulmus, Query, Common\EntityResolver, Repository, Event, Common\Sql
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use Ulmus\Attribute\Property\{
 | 
			
		||||
@ -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)) {
 | 
			
		||||
@ -276,11 +276,16 @@ class RelationBuilder
 | 
			
		||||
            if ( $relation->generateKey ) {
 | 
			
		||||
                $value = call_user_func_array($relation->generateKey, [ $this->entity ]);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
            elseif (isset($this->entity->$field)) {
 | 
			
		||||
                $value = $this->entity->$field;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), $value );
 | 
			
		||||
            if (isset($value)) {
 | 
			
		||||
                $this->repository->where( is_object($relation->foreignKey) ? $relation->foreignKey : $baseEntity::field($relation->foreignKey), $value );
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                $this->repository->where(Sql::raw('TRUE'), Sql::raw('FALSE'));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->applyFilter($this->repository, $name);
 | 
			
		||||
@ -340,7 +345,7 @@ class RelationBuilder
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    protected function fieldIsNullable(string $name) : bool
 | 
			
		||||
    {
 | 
			
		||||
        return $this->resolver->reflectedClass->getProperties(true)[$name]->allowsNull();
 | 
			
		||||
 | 
			
		||||
@ -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