resetVirtualProperties(); } #[Ignore] public function entityFillFromDataset(iterable $dataset, bool $overwriteDataset = false) : self { $loaded = $this->isLoaded(); $entityResolver = $this->resolveEntity(); foreach($dataset as $key => $value) { $field = $entityResolver->field(strtolower($key), EntityResolver::KEY_COLUMN_NAME, false) ?? null; $field ??= $entityResolver->field(strtolower($key), EntityResolver::KEY_LC_ENTITY_NAME, false); if ( $field === null ) { if ($this->entityStrictFieldsDeclaration ) { throw new \Exception("Field `$key` can not be found within your entity ".static::class); } else { $this->entityDatasetUnmatchedFields[$key] = $value; } } elseif ( is_null($value) ) { $this->{$field['name']} = null; } elseif ( $field['type'] === 'array' ) { if ( is_string($value)) { if (substr($value, 0, 1) === "a") { $this->{$field['name']} = unserialize($value); } else { $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))); } $this->{$field['name']} = $data; } } elseif ( is_array($value) ) { $this->{$field['name']} = $value; } } elseif ( EntityField::isScalarType($field['type']) ) { if ( $field['type'] === 'string' ) { $annotation = $entityResolver->searchFieldAnnotation($field['name'], [ Attribute\Property\Field::class, Field::class ] ); if ( $annotation->length ?? null ) { $value = mb_substr($value, 0, $annotation->length); } } elseif ( $field['type'] === 'bool' ) { $value = (bool) $value; } $this->{$field['name']} = $value; } elseif ( $value instanceof \UnitEnum ) { $this->{$field['name']} = $value; } elseif (enum_exists($field['type'])) { $this->{$field['name']} = $field['type']::from($value); } elseif ( ! $field['builtin'] ) { try { $this->{$field['name']} = Ulmus::instanciateObject($field['type'], [ $value ]); } catch(\Error $e) { $f = $field['type']; throw new \Error(sprintf("%s for class '%s' on field '%s'", $e->getMessage(), get_class($this), $field['name'])); } } # Keeping original data to diff on UPDATE query if ( ! $loaded /* || $isLoadedDataset */ ) { #if ( $field !== null ) { # $annotation = $entityResolver->searchFieldAnnotation($field['name'], new Field() ); # $this->entityLoadedDataset[$annotation ? $annotation->name : $field['name']] = $dataset; # <--------- THIS TO FIX !!!!!! #} $this->entityLoadedDataset = array_change_key_case($dataset, \CASE_LOWER); } elseif ($overwriteDataset) { $this->entityLoadedDataset = array_change_key_case($dataset, \CASE_LOWER) + $this->entityLoadedDataset; } } return $this; } #[Ignore] 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; } #[Ignore] public function fromArray(iterable $dataset) : self { return $this->entityFillFromDataset($dataset); } public function entityGetDataset(bool $includeRelations = false, bool $returnSource = false) : array { if ( $returnSource ) { return $this->entityLoadedDataset; } $dataset = []; $entityResolver = $this->resolveEntity(); foreach($entityResolver->fieldList(Common\EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) { $annotation = $entityResolver->searchFieldAnnotation($key, [ Attribute\Property\Field::class, Field::class ]); if ( isset($this->$key) ) { $dataset[$annotation->name ?? $key] = static::repository()->adapter->adapter()->writableValue($this->$key); } elseif ( $field['nullable'] ) { $dataset[$annotation->name ?? $key] = null; } } # @TODO Must fix recursive bug ! if ($includeRelations) { foreach($entityResolver->properties as $name => $field){ $relation = $entityResolver->searchFieldAnnotation($key, [ Attribute\Property\Relation::class. Relation::class ] ); if ( $relation && isset($this->$name) && ($relation->entity ?? $relation->bridge) !== static::class ) { if ( null !== $value = $this->$name ?? null ) { if ( is_iterable($value) ) { $list = []; foreach($value as $entity) { $list[] = $entity->entityGetDataset(false); } $dataset[$name] = $list; } elseif ( is_object($value) ) { $dataset[$name] = $value->entityGetDataset(false); } } } } } return $dataset; } #[Ignore] public function toArray($includeRelations = false, array $filterFields = null) : array { $dataset = $this->entityGetDataset($includeRelations); return $filterFields ? array_intersect_key($dataset, array_flip($filterFields)) : $dataset; } #[Ignore] public function toCollection() : EntityCollection { return static::entityCollection([ $this ]); } #[Ignore] public function isLoaded() : bool { if (empty($this->entityLoadedDataset)) { return false; } if ( null === $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) { throw new Exception\EntityPrimaryKeyUnknown(sprintf("Entity %s has no field containing attributes 'primary_key'", static::class)); } $key = key($pkField); return isset($this->$key); } #[Ignore] public function __get(string $name) { $relation = new Repository\RelationBuilder($this); if ( false !== $data = $relation->searchRelation($name) ) { return $this->$name = $data; } throw new \Exception(sprintf("[%s] - Undefined variable: %s", static::class, $name)); } #[Ignore] public function __isset(string $name) : bool { #if ( null !== $relation = static::resolveEntity()->searchFieldAnnotation($name, new Relation() ) ) { # return isset($this->{$relation->key}); #} $rel = static::resolveEntity()->searchFieldAnnotation($name, [ Attribute\Property\Relation::class, Relation::class ]); if ( $this->isLoaded() && $rel ) { return true; } return isset($this->$name); } #[Ignore] public function __sleep() { return array_merge(array_keys($this->resolveEntity()->fieldList()), [ 'entityLoadedDataset' ] ); } #[Ignore] public function __wakeup() { $this->resetVirtualProperties(); } #[Ignore] public function __clone() { foreach($this as $prop) { } if ( null !== $pkField = $this->resolveEntity()->getPrimaryKeyField($this) ) { $key = key($pkField); unset($this->$key); } } #[Ignore] public function jsonSerialize() : mixed { return $this->entityGetDataset(); } #[Ignore] public static function resolveEntity() : EntityResolver { return Ulmus::resolveEntity(static::class); } #[Ignore] public static function repository(string $alias = Repository::DEFAULT_ALIAS, ConnectionAdapter $adapter = null) : Repository { return Ulmus::repository(static::class, $alias, $adapter); } #[Ignore] public static function entityCollection(...$arguments) : EntityCollection { $collection = new EntityCollection(...$arguments); $collection->entityClass = static::class; return $collection; } #[Ignore] public static function queryBuilder() : QueryBuilder { return Ulmus::queryBuilder(static::class); } #[Ignore] public static function field($name, null|string|bool $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)); } #[Ignore] public static function fields(array $fields, null|string|bool $alias = Repository::DEFAULT_ALIAS, string $separator = ', ') : string { return implode($separator, array_map(function($item) use ($alias){ return static::field($item, $alias); }, $fields)); } #[Ignore] public static function searchRequest(...$arguments) : SearchRequestInterface { return new class() implements SearchRequestInterface { use SearchRequestPaginationTrait; public function fromRequest(ServerRequestInterface $request) : self { $get = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return $i !== ""; })); $this->page = $get->offsetExists('page') ? $get['page'] : 1; $this->limit = 100; return $this; } public function filter(Repository $repository) : Repository { return $repository; } public function wheres() : iterable { return array_filter([ ], fn($i) => ! is_null($i) ) + [ ]; } public function likes(): iterable { return array_filter([ ], fn($i) => ! is_null($i) ) + []; } public function groups(): iterable { return []; } public function orders(): iterable { return array_filter([ ], fn($e) => ! is_null($e) && $e !== "" ); } }; } }