Merge branch 'master' of https://git.mcnd.ca/mcndave/ulmus
This commit is contained in:
		
						commit
						531908b04c
					
				@ -84,7 +84,7 @@ class MsSQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
 | 
			
		||||
            $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
 | 
			
		||||
            $pdo->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
 | 
			
		||||
        }
 | 
			
		||||
        catch(PDOException $ex){
 | 
			
		||||
        catch(\PDOException $ex){
 | 
			
		||||
            throw $ex;
 | 
			
		||||
        }
 | 
			
		||||
        finally {
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,7 @@ class MySQL implements AdapterInterface, MigrateInterface, SqlAdapterInterface {
 | 
			
		||||
            $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
 | 
			
		||||
            $pdo->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
 | 
			
		||||
        }
 | 
			
		||||
        catch(PDOException $ex){
 | 
			
		||||
        catch(\PDOException $ex){
 | 
			
		||||
            throw $ex;
 | 
			
		||||
        }
 | 
			
		||||
        finally {
 | 
			
		||||
 | 
			
		||||
@ -19,22 +19,11 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
 | 
			
		||||
 | 
			
		||||
    const DSN_PREFIX = "sqlite";
 | 
			
		||||
 | 
			
		||||
    public string $path;
 | 
			
		||||
 | 
			
		||||
    public array $pragma;
 | 
			
		||||
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        ? string $path = null,
 | 
			
		||||
        ? array $pragma = null
 | 
			
		||||
    ) {
 | 
			
		||||
        if ($path !== null) {
 | 
			
		||||
            $this->path = $path;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($pragma !== null) {
 | 
			
		||||
            $this->pragma = $pragma;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
        public null|string $path = null,
 | 
			
		||||
        public null|array $pragmaBegin = null,
 | 
			
		||||
        public null|array $pragmaClose = null,
 | 
			
		||||
    ) { }
 | 
			
		||||
 | 
			
		||||
    public function connect() : PdoObject
 | 
			
		||||
    {
 | 
			
		||||
@ -44,9 +33,14 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
 | 
			
		||||
            $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
 | 
			
		||||
            $pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
            $pdo->onClose = function(PdoObject $obj) {
 | 
			
		||||
                static::registerPragma($obj, $this->pragmaClose);
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            $this->exportFunctions($pdo);
 | 
			
		||||
            $this->registerPragma($pdo, $this->pragmaBegin);
 | 
			
		||||
        }
 | 
			
		||||
        catch(PDOException $ex){
 | 
			
		||||
        catch(\PDOException $ex){
 | 
			
		||||
            throw $ex;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -63,13 +57,15 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
 | 
			
		||||
    public function setup(array $configuration) : void
 | 
			
		||||
    {
 | 
			
		||||
        $this->path = $configuration['path'] ?? "";
 | 
			
		||||
        $this->pragma = $configuration['pragma'] ?? [];
 | 
			
		||||
        $this->pragmaBegin = array_filter($configuration['pragma_begin'] ?? []);
 | 
			
		||||
        $this->pragmaClose = array_filter($configuration['pragma_close'] ?? []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # https://sqlite.org/lang_keywords.html
 | 
			
		||||
    public function escapeIdentifier(string $segment, int $type) : string 
 | 
			
		||||
    {
 | 
			
		||||
        switch($type) {
 | 
			
		||||
            default:
 | 
			
		||||
            case static::IDENTIFIER_DATABASE:
 | 
			
		||||
            case static::IDENTIFIER_TABLE:
 | 
			
		||||
            case static::IDENTIFIER_FIELD:
 | 
			
		||||
@ -194,6 +190,24 @@ class SQLite implements AdapterInterface, MigrateInterface, SqlAdapterInterface
 | 
			
		||||
        $pdo->sqliteCreateFunction('year', fn($date) => ( new \DateTime($date) )->format('Y'), 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function registerPragma(PdoObject $pdo, array $pragmaList) : void
 | 
			
		||||
    {
 | 
			
		||||
        $builder = new QueryBuilder\SqliteQueryBuilder();
 | 
			
		||||
 | 
			
		||||
        foreach($pragmaList as $pragma) {
 | 
			
		||||
            list($key, $value) = explode('=', $pragma) + [ null, null ];
 | 
			
		||||
 | 
			
		||||
            $sql = $builder->pragma($key, $value)->render();
 | 
			
		||||
            $query = $pdo->query($sql);
 | 
			
		||||
 | 
			
		||||
            if ( ! $query->execute() ) {
 | 
			
		||||
                throw new \InvalidArgumentException(sprintf("Pragma query could not be executed : %s", $sql));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $builder->reset();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generateAlterColumn(FieldDefinition $definition, array $field) : string|\Stringable
 | 
			
		||||
    {
 | 
			
		||||
        return implode(" ", [
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ class Field implements \Notes\Annotation {
 | 
			
		||||
 | 
			
		||||
    public string $name;
 | 
			
		||||
    
 | 
			
		||||
    public int $length;
 | 
			
		||||
    public int|string $length;
 | 
			
		||||
 | 
			
		||||
    public int $precision;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ use Ulmus\Attribute\ConstrainActionEnum;
 | 
			
		||||
class ForeignKey extends PrimaryKey {
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public ? string $name = null,
 | 
			
		||||
        public ? string $type = 'bigint',
 | 
			
		||||
        public ? string $type = null,
 | 
			
		||||
        public null|int|string $length = null,
 | 
			
		||||
        public ? int $precision = null,
 | 
			
		||||
        public array $attributes = [
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,8 @@ class PdoObject extends PDO {
 | 
			
		||||
 | 
			
		||||
    public mixed $lastInsertId = null;
 | 
			
		||||
 | 
			
		||||
    public \Closure $onClose;
 | 
			
		||||
 | 
			
		||||
    public function select(string $sql, array $parameters = []): PDOStatement
 | 
			
		||||
    {
 | 
			
		||||
        static::$dump && call_user_func_array(static::$dump, [ $sql, $parameters ]);
 | 
			
		||||
@ -32,6 +34,13 @@ class PdoObject extends PDO {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __destruct()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->onClose ?? null) {
 | 
			
		||||
            call_user_func($this->onClose, $this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function runQuery(string $sql, array $parameters = []): ? static
 | 
			
		||||
    {
 | 
			
		||||
        static::$dump && call_user_func_array(static::$dump, [ $sql, $parameters ]);
 | 
			
		||||
 | 
			
		||||
@ -62,7 +62,7 @@ trait EntityTrait {
 | 
			
		||||
                        $data = json_decode($value, true);
 | 
			
		||||
 | 
			
		||||
                        if (json_last_error() !== \JSON_ERROR_NONE) {
 | 
			
		||||
                            throw new \Exception(sprintf("JSON error while decoding in EntityTrait : '%s' given %s with field %s", json_last_error_msg(), $value, json_encode($field)));
 | 
			
		||||
                            throw new \Exception(sprintf("JSON error while decoding in EntityTrait : '%s' given %s", json_last_error_msg(), $value));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        $this->{$field['name']} = $data;
 | 
			
		||||
@ -112,7 +112,7 @@ trait EntityTrait {
 | 
			
		||||
                $this->entityLoadedDataset = array_change_key_case($dataset, \CASE_LOWER);
 | 
			
		||||
            }
 | 
			
		||||
            elseif ($overwriteDataset) {
 | 
			
		||||
                $this->entityLoadedDataset = array_change_key_case($dataset, \CASE_LOWER) + $this->entityLoadedDataset;
 | 
			
		||||
                $this->entityLoadedDataset = iterator_to_array(array_change_key_case($dataset, \CASE_LOWER)) + $this->entityLoadedDataset;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
@ -123,14 +123,12 @@ trait EntityTrait {
 | 
			
		||||
    public function resetVirtualProperties() : self
 | 
			
		||||
    {
 | 
			
		||||
        foreach($this->resolveEntity()->properties as $prop => $property) {
 | 
			
		||||
            if ( empty($property['builtin']) ) {
 | 
			
		||||
            foreach($property['tags'] as $tag) {
 | 
			
		||||
                if ( in_array(strtolower($tag['tag']), [ 'relation', 'join', 'virtual' ] ) ) {
 | 
			
		||||
                    unset($this->$prop);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
@ -264,10 +262,6 @@ trait EntityTrait {
 | 
			
		||||
    #[Ignore]
 | 
			
		||||
    public function __clone()
 | 
			
		||||
    {
 | 
			
		||||
        foreach($this as $prop) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( null !== $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) {
 | 
			
		||||
            $key = key($pkField);
 | 
			
		||||
 | 
			
		||||
@ -309,9 +303,12 @@ trait EntityTrait {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[Ignore]
 | 
			
		||||
    public static function field($name, null|string|bool $alias = Repository::DEFAULT_ALIAS) : EntityField
 | 
			
		||||
    public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField
 | 
			
		||||
    {
 | 
			
		||||
        return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : ( $alias === false ? '' : Repository::DEFAULT_ALIAS ), Ulmus::resolveEntity(static::class));
 | 
			
		||||
 | 
			
		||||
        $default = ( $alias === false ? '' : Repository::DEFAULT_ALIAS ); # bw compatibility, to be deprecated
 | 
			
		||||
 | 
			
		||||
        return new EntityField(static::class, $name, $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : $default, Ulmus::resolveEntity(static::class));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[Ignore]
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,7 @@ class Where extends Fragment {
 | 
			
		||||
        $this->parent = $queryBuilder->where ?? null;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public function add($field, $value, string $operator, string $condition, bool $not = false) : self
 | 
			
		||||
    public function add($field, mixed $value, string $operator, string $condition, bool $not = false) : self
 | 
			
		||||
    {
 | 
			
		||||
        $this->validateFieldType($field);
 | 
			
		||||
        # $this->validateValueType($value);
 | 
			
		||||
@ -73,7 +73,7 @@ class Where extends Fragment {
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function whereCondition($field, $value, string $operator = self::OPERATOR_EQUAL, string $condition = self::CONDITION_AND, bool $not = false) {
 | 
			
		||||
    protected function whereCondition($field, mixed $value, string $operator = self::OPERATOR_EQUAL, string $condition = self::CONDITION_AND, bool $not = false) {
 | 
			
		||||
        return new class($this->queryBuilder, $field, $value, $operator, $condition, $not) {
 | 
			
		||||
 | 
			
		||||
            public mixed $value;
 | 
			
		||||
 | 
			
		||||
@ -259,6 +259,11 @@ class QueryBuilder implements Query\QueryBuilderInterface
 | 
			
		||||
        if ( null === $offset = $this->getFragment(Query\Offset::class) ) {
 | 
			
		||||
            $offset = new Query\Offset();
 | 
			
		||||
            $this->push($offset);
 | 
			
		||||
 | 
			
		||||
            # A limit is required to match an offset
 | 
			
		||||
            if ( null === $limit = $this->getFragment(Query\Limit::class) ) {
 | 
			
		||||
                $this->limit(\PHP_INT_MAX);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $offset->set($value);
 | 
			
		||||
 | 
			
		||||
@ -114,7 +114,7 @@ class Repository
 | 
			
		||||
            throw new Exception\EntityPrimaryKeyUnknown("A primary key value has to be defined to delete an item.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return (bool) $this->wherePrimaryKey($value)->deleteOne()->rowCount;
 | 
			
		||||
        return (bool) $this->wherePrimaryKey($value, null)->deleteOne()->rowCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function destroy(object $entity) : bool
 | 
			
		||||
@ -144,6 +144,17 @@ class Repository
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function clone(object|array $entity) : object|false
 | 
			
		||||
    {
 | 
			
		||||
        $entity = is_object($entity) ? clone $entity : $entity;
 | 
			
		||||
 | 
			
		||||
        foreach(Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField() as $key => $field) {
 | 
			
		||||
            unset($entity->$key);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->save($entity) ? $entity : false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public function save(object|array $entity, ? array $fieldsAndValue = null, bool $replace = false) : bool
 | 
			
		||||
    {
 | 
			
		||||
        if ( is_array($entity) ) {
 | 
			
		||||
@ -588,7 +599,7 @@ class Repository
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function wherePrimaryKey(mixed $value) : self
 | 
			
		||||
    public function wherePrimaryKey(mixed $value, null|string|bool $alias = self::DEFAULT_ALIAS) : self
 | 
			
		||||
    {
 | 
			
		||||
        if ( null === $primaryKeyField = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField() ) {
 | 
			
		||||
            throw new Exception\EntityPrimaryKeyUnknown("Entity has no field containing attributes 'primary_key'");
 | 
			
		||||
@ -596,7 +607,7 @@ class Repository
 | 
			
		||||
 | 
			
		||||
        $pkField = key($primaryKeyField);
 | 
			
		||||
 | 
			
		||||
        return $this->where($this->entityClass::field($primaryKeyField[$pkField]->name ?? $pkField), $value);
 | 
			
		||||
        return $this->where($this->entityClass::field($primaryKeyField[$pkField]->name ?? $pkField, false), $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function withJoin(string|array $fields, array $options = []) : self
 | 
			
		||||
 | 
			
		||||
@ -47,7 +47,7 @@ class RelationBuilder
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function searchRelation(string $name) : object|bool
 | 
			
		||||
    public function searchRelation(string $name) : mixed
 | 
			
		||||
    {
 | 
			
		||||
        # Resolve relations here if one is called
 | 
			
		||||
        if ( $this->entity->isLoaded() ) {
 | 
			
		||||
@ -55,7 +55,13 @@ class RelationBuilder
 | 
			
		||||
                return $dataset;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return $this->resolveRelation($name) ?: $this->resolveVirtual($name) ?: false;
 | 
			
		||||
            if ( false !== $value = $this->resolveRelation($name) ) {
 | 
			
		||||
            return $value;
 | 
			
		||||
            }
 | 
			
		||||
            elseif (  false !== $value = $this->resolveVirtual($name) ) {
 | 
			
		||||
                                            return $value;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            if ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class , Relation::class ] ) ) {
 | 
			
		||||
@ -69,7 +75,7 @@ class RelationBuilder
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function resolveVirtual(string $name) : bool|object
 | 
			
		||||
    protected function resolveVirtual(string $name) : mixed
 | 
			
		||||
    {
 | 
			
		||||
        if (null !== ($virtual = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Virtual::class, Annotation\Property\Virtual::class ]))) {
 | 
			
		||||
            if ($virtual->closure ?? false) {
 | 
			
		||||
@ -82,7 +88,7 @@ class RelationBuilder
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function resolveRelation(string $name) : bool|object
 | 
			
		||||
    protected function resolveRelation(string $name) : mixed
 | 
			
		||||
    {
 | 
			
		||||
        if ( null !== ( $relation = $this->resolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ] ) ) ) {
 | 
			
		||||
            $this->orders = $this->resolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class, OrderBy::class ] );
 | 
			
		||||
@ -116,7 +122,7 @@ class RelationBuilder
 | 
			
		||||
 | 
			
		||||
                    $this->entity->eventExecute(Event\EntityRelationLoadInterface::class, $name, $this->repository);
 | 
			
		||||
 | 
			
		||||
                    $results = call_user_func([ $this->repository, 'loadAll' ]);
 | 
			
		||||
                    $results = call_user_func([ $this->repository, $relationRelation->function() ]);
 | 
			
		||||
 | 
			
		||||
                    if ($relation->bridgeField ?? false) {
 | 
			
		||||
                        $collection = $relation->bridge::entityCollection();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user