From f938e18572fbd8b8f697fcc8220e76fc6b59382a Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Fri, 24 Apr 2026 19:26:02 +0000 Subject: [PATCH] - Added a 'select' attribute, WIP on ArrayCollection and ArrayOf also --- src/Attribute/Property/ArrayOf.php | 2 +- src/Attribute/Property/Field/Select.php | 10 +++++++ src/Common/EntityField.php | 36 +++++++++++++------------ src/Entity/ArrayCollection.php | 7 ++++- src/EntityTrait.php | 12 ++++----- src/Repository.php | 21 +++++++++------ src/Repository/QueryBuildingTrait.php | 2 +- 7 files changed, 56 insertions(+), 34 deletions(-) create mode 100644 src/Attribute/Property/Field/Select.php diff --git a/src/Attribute/Property/ArrayOf.php b/src/Attribute/Property/ArrayOf.php index ef3305a..3e4446f 100644 --- a/src/Attribute/Property/ArrayOf.php +++ b/src/Attribute/Property/ArrayOf.php @@ -10,7 +10,7 @@ use Ulmus\Entity\JsonUnserializable; class ArrayOf implements \Ulmus\Entity\EntityValueModifier { public function __construct( - protected string $type, + public readonly string $type, ) {} public function push(mixed $value, ReflectedProperty $property): mixed diff --git a/src/Attribute/Property/Field/Select.php b/src/Attribute/Property/Field/Select.php new file mode 100644 index 0000000..8462dcc --- /dev/null +++ b/src/Attribute/Property/Field/Select.php @@ -0,0 +1,10 @@ +entityClass = $entityClass; - $this->name = $name; - $this->alias = $alias; - $this->entityResolver = $resolver; - } + public function __construct( + public string $name, + public string $entityClass, + public ? string $alias, + protected EntityResolver $entityResolver, + protected bool $select = false + ) {} public function name($useAlias = true) : string { $name = $this->entityResolver->searchFieldAnnotation($this->name, [ Field::class ] )->name ?? $this->name; - + $name = $this->entityResolver->databaseAdapter()->adapter()->escapeIdentifier($name, AdapterInterface::IDENTIFIER_FIELD); - return $useAlias && $this->alias ? "{$this->alias}.$name" : $name; + $fullname = $useAlias && $this->alias ? "{$this->alias}.$name" : $name; + + if ($this->select) { + $select = $this->entityResolver->searchFieldAnnotation($this->name, [Field\Select::class]); + + if ($select) { + return "(" . str_replace(['%NAME%'], [$fullname], $select->sql) . ") AS code"; + } + } + + return $fullname; } public static function isScalarType($type) : bool diff --git a/src/Entity/ArrayCollection.php b/src/Entity/ArrayCollection.php index 7d1c892..b2ee592 100644 --- a/src/Entity/ArrayCollection.php +++ b/src/Entity/ArrayCollection.php @@ -9,7 +9,12 @@ class ArrayCollection extends EntityCollection implements JsonUnserializable public function jsonUnserialize(array $json): void { - $this->fromarray($json); + $this->fromArray($json); + } + + public function jsonSerialize(): mixed + { + return $this->getArrayCopy(); } public function fromArray(array $datasets, ? string /*stringable*/ $entityClass = null) : self diff --git a/src/EntityTrait.php b/src/EntityTrait.php index c8c5f20..23c62b3 100644 --- a/src/EntityTrait.php +++ b/src/EntityTrait.php @@ -309,21 +309,21 @@ trait EntityTrait { } #[Ignore] - public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS) : EntityField + public static function field($name, null|string|false $alias = Repository::DEFAULT_ALIAS, bool $select = false) : EntityField { $default = ( $alias === false ? '' : static::repository()::DEFAULT_ALIAS ); # bw compatibility, to be deprecated $alias = $alias ? Ulmus::repository(static::class)->adapter->adapter()->escapeIdentifier($alias, Adapter\AdapterInterface::IDENTIFIER_FIELD) : $default; - return new EntityField(static::class, $name, $alias, Ulmus::resolveEntity(static::class)); + return new EntityField( + entityClass: static::class, name: $name, alias: $alias, entityResolver: Ulmus::resolveEntity(static::class), select: $select + ); } #[Ignore] - public static function fields(array $fields, null|string|false $alias = Repository::DEFAULT_ALIAS, string $separator = ', ') : string + public static function fields(array $fields, null|string|false $alias = Repository::DEFAULT_ALIAS, string $separator = ', ', bool $select = false) : string { - return implode($separator, array_map(function($item) use ($alias){ - return static::field($item, $alias); - }, $fields)); + return implode($separator, array_map(fn($e) => static::field($e, $alias, $select), $fields)); } #[Ignore] diff --git a/src/Repository.php b/src/Repository.php index 286c34d..abc8654 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -59,7 +59,7 @@ class Repository implements RepositoryInterface public function loadOne() : ? object { - return $this->limit(1)->selectSqlQuery()->collectionFromQuery()[0] ?? null; + return $this->limit(1)->loadAll()[0] ?? null; } public function loadOneFromField($field, $value) : ? object @@ -72,11 +72,12 @@ class Repository implements RepositoryInterface return $primaryKey ? $this->loadOneFromField($primaryKey, $value) : $this->wherePrimaryKey($value)->loadOne(); } - public function loadAll () : EntityCollection + public function loadAll() : EntityCollection { return $this->selectSqlQuery()->collectionFromQuery(); } + #[\Deprecated(message: "loadFromField is now to be used instead of this method")] public function loadAllFromField($field, $value, $operator= Query\Where::OPERATOR_EQUAL) : EntityCollection { return $this->loadFromField($field, $value, $operator); @@ -84,7 +85,7 @@ class Repository implements RepositoryInterface public function loadFromField($field, $value, $operator= Query\Where::OPERATOR_EQUAL) : EntityCollection { - return $this->selectSqlQuery()->where($field, $value, $operator)->collectionFromQuery(); + return $this->where($field, $value, $operator)->loadAll(); } public function count() : int @@ -245,8 +246,6 @@ class Repository implements RepositoryInterface $diff = $fieldsAndValue ?? $this->generateWritableDataset($entity); -# dump($diff); - if ( [] !== $diff ) { if ($primaryKeyDefinition) { $pkField = key($primaryKeyDefinition); @@ -263,7 +262,6 @@ class Repository implements RepositoryInterface $entity->entityFillFromDataset($dataset, true); - # $fieldsAndValue ??= &$dataset; $this->eventExecute(Event\Query\Update::class, $this, $entity, $dataset, $replace); return $update ? (bool) $update->rowCount : false; @@ -400,6 +398,7 @@ class Repository implements RepositoryInterface foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) { + # @TODO add handling of the new #[Select] attribute here ! $this->select(sprintf("%s.$key as {$prependField}{$field->name}", $this->escapeIdentifier($alias))); } } @@ -456,7 +455,7 @@ class Repository implements RepositoryInterface if ( $canSelect ) { $select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true); - $this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select))); + $this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select), select: true)); } $fields = (array) $fields; @@ -488,6 +487,8 @@ class Repository implements RepositoryInterface $name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name; if ($canSelect) { + # @TODO add handling of the new #[Select] attribute here ! + $this->select("$escAlias.$fieldName as $alias\${$name}"); } } @@ -563,7 +564,11 @@ class Repository implements RepositoryInterface } if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) { - $this->select($this->escapeIdentifier($this->alias) . ".*"); + # @TODO add handling of the new #[Select] attribute here ! + $select = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true); + $this->select($this->entityClass::fields(array_map(fn($f) => $f['object']->name ?? $f['name'], $select), alias: $this->alias, select: true)); + + # $this->select($this->escapeIdentifier($this->alias) . ".*"); } # Apply FILTER annotation to this too ! diff --git a/src/Repository/QueryBuildingTrait.php b/src/Repository/QueryBuildingTrait.php index 69d1571..878f910 100644 --- a/src/Repository/QueryBuildingTrait.php +++ b/src/Repository/QueryBuildingTrait.php @@ -222,7 +222,7 @@ trait QueryBuildingTrait { if ( null === $this->queryBuilder->getFragment(Query\Select::class) ) { $fields = $this->entityResolver->fieldList(EntityResolver::KEY_COLUMN_NAME, true); - $this->select($this->entityClass::fields(array_map(fn($f) => $f->object->name ?? $f->name, $fields))); + $this->select($this->entityClass::fields(fields: array_map(fn($f) => $f->object->name ?? $f->name, $fields), select: true)); } if ( null === $this->queryBuilder->getFragment(Query\From::class) ) {