From bd9078230d0c4a99567b665e8c9c0d1ee29576a2 Mon Sep 17 00:00:00 2001 From: Dave Mc Nicoll Date: Fri, 31 May 2024 12:26:39 +0000 Subject: [PATCH] - Multiple bug fixes related to notes 2.x --- src/Attribute/Property/Relation.php | 2 +- src/Common/EntityField.php | 6 +- src/Common/EntityResolver.php | 18 ++--- src/Entity/DatasetHandler.php | 12 +-- src/Query/Alter.php | 3 +- src/Query/Create.php | 3 +- src/Repository.php | 80 ++++++++++--------- src/Repository/RelationBuilder.php | 14 ++-- .../SearchRequestFromRequestTrait.php | 4 + 9 files changed, 69 insertions(+), 73 deletions(-) diff --git a/src/Attribute/Property/Relation.php b/src/Attribute/Property/Relation.php index 083bbf4..9aca8cc 100644 --- a/src/Attribute/Property/Relation.php +++ b/src/Attribute/Property/Relation.php @@ -35,7 +35,7 @@ class Relation implements ResettablePropertyInterface { try { $e = $this->entity; } catch (\Throwable $ex) { - throw new \Exception("Your @Relation annotation seems to be missing an `entity` entry."); + throw new \Exception("Your @Relation attribute seems to be missing an `entity` entry."); } return new $e(); diff --git a/src/Common/EntityField.php b/src/Common/EntityField.php index 2cdddb7..0d64c54 100644 --- a/src/Common/EntityField.php +++ b/src/Common/EntityField.php @@ -2,12 +2,10 @@ namespace Ulmus\Common; -use Ulmus\Annotation\Annotation; -use Ulmus\Attribute; +use Ulmus\Attribute\Property\{ Field }; use Ulmus\Migration\FieldDefinition; use Ulmus\Ulmus, Ulmus\Adapter\AdapterInterface, - Ulmus\Annotation\Property\Field, Ulmus\Query\WhereRawParameter; class EntityField implements WhereRawParameter @@ -30,7 +28,7 @@ class EntityField implements WhereRawParameter public function name($useAlias = true) : string { - $name = $this->entityResolver->searchFieldAnnotation($this->name, [ Attribute\Property\Field::class, Field::class ] )->name ?? $this->name; + $name = $this->entityResolver->searchFieldAnnotation($this->name, [ Field::class ] )->name ?? $this->name; $name = $this->entityResolver->databaseAdapter()->adapter()->escapeIdentifier($name, AdapterInterface::IDENTIFIER_FIELD); diff --git a/src/Common/EntityResolver.php b/src/Common/EntityResolver.php index 92bd6dc..2dfb063 100644 --- a/src/Common/EntityResolver.php +++ b/src/Common/EntityResolver.php @@ -12,6 +12,8 @@ use Ulmus\Ulmus, Ulmus\Attribute\Property\Relation, Ulmus\Attribute\Property\Virtual; +use Notes\Common\ReflectedAttribute; + use Notes\ObjectReflection; class EntityResolver { @@ -118,7 +120,7 @@ class EntityResolver { return $this->searchFieldAnnotationList($field, $annotationType, $caseSensitive)[0] ?? null; } - public function searchFieldAnnotationList(string $field, array|object|string $annotationType, bool $caseSensitive = true) : array + public function searchFieldAnnotationList(string $field, array|object|string $attributeType, bool $caseSensitive = true) : false|array { $list = []; @@ -126,19 +128,11 @@ class EntityResolver { $search = $caseSensitive ? $properties : array_change_key_case($properties, \CASE_LOWER); - $annotations = is_array($annotationType) ? $annotationType : [ $annotationType ]; - if ( null !== ( $search[$field] ?? null ) ) { - foreach($search[$field]->getAttributes() as $tag) { - foreach($annotations as $annotation) { - if ( $tag->object instanceof $annotation ) { - $list[] = $tag->object; - } - } - } + return array_map(fn(ReflectedAttribute $e) => $e->object, $search[$field]->getAttributes((array) $attributeType)); } - - return $list; + + return false; } public function tableName($required = false) : string diff --git a/src/Entity/DatasetHandler.php b/src/Entity/DatasetHandler.php index fc39230..f51f9c2 100644 --- a/src/Entity/DatasetHandler.php +++ b/src/Entity/DatasetHandler.php @@ -20,13 +20,13 @@ class DatasetHandler public function pull(object $entity) : Generator { foreach($this->entityResolver->fieldList(EntityResolver::KEY_ENTITY_NAME, true) as $key => $field) { - $annotation = $this->entityResolver->searchFieldAnnotation($key,[ Field::class ]); + $attribute = $this->entityResolver->searchFieldAnnotation($key,[ Field::class ]); if ( $entity->__isset($key) ) { - yield $annotation->name ?? $key => $entity->$key; + yield $attribute->name ?? $key => $entity->$key; } elseif ( $field->allowsNull() ) { - yield $annotation->name ?? $key => null; + yield $attribute->name ?? $key => null; } } } @@ -77,10 +77,10 @@ class DatasetHandler elseif ( EntityField::isScalarType($type->type) ) { if ( $type->type === 'string' ) { - $annotation = $this->entityResolver->searchFieldAnnotation($field->name, [ Field::class ] ); + $attribute = $this->entityResolver->searchFieldAnnotation($field->name, [ Field::class ] ); - if ( $annotation->length ?? null ) { - $value = mb_substr($value, 0, $annotation->length); + if ( $attribute->length ?? null ) { + $value = mb_substr($value, 0, $attribute->length); } } elseif ( $type->type === 'bool' ) { diff --git a/src/Query/Alter.php b/src/Query/Alter.php index 4fa0f9a..d1e5017 100644 --- a/src/Query/Alter.php +++ b/src/Query/Alter.php @@ -3,8 +3,7 @@ namespace Ulmus\Query; use Ulmus\Adapter\AdapterInterface; -use Ulmus\Annotation, - Ulmus\Common\EntityField; +use Ulmus\Common\EntityField; class Alter extends Fragment { diff --git a/src/Query/Create.php b/src/Query/Create.php index 6e95a1d..1911a4c 100644 --- a/src/Query/Create.php +++ b/src/Query/Create.php @@ -3,8 +3,7 @@ namespace Ulmus\Query; use Ulmus\Adapter\AdapterInterface; -use Ulmus\Annotation, - Ulmus\Common\EntityField; +use Ulmus\Common\EntityField; class Create extends Fragment { diff --git a/src/Repository.php b/src/Repository.php index aea8c98..f8a8de9 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -2,16 +2,9 @@ namespace Ulmus; -use Ulmus\Annotation\Property\{Field, - OrderBy, - Where, - Having, - Relation, - Filter, - Join, - FilterJoin, - WithJoin, - Relation\Ignore as RelationIgnore}; +use Ulmus\Attribute\Property\{ + Field, OrderBy, Where, Having, Relation, Filter, Join, FilterJoin, WithJoin +}; use Ulmus\Common\EntityResolver; use Ulmus\Repository\WithOptionEnum; @@ -88,7 +81,16 @@ class Repository $this->select( "DISTINCT COUNT(*) OVER ()" ); } else { - $this->select(Common\Sql::function("COUNT", '*')); + $pk = Ulmus::resolveEntity($this->entityClass)->getPrimaryKeyField(); + + if (count($pk) === 1) { + $field = key($pk); + + $this->select(Common\Sql::function('COUNT', Common\Sql::raw("DISTINCT " . $this->entityClass::field($field)))); + } + else { + $this->select(Common\Sql::function('COUNT', Common\Sql::raw('*'))); + } } $this->selectSqlQuery(); @@ -362,7 +364,7 @@ class Repository $dataset = $this->generateDatasetDiff($entity, $oldValues); foreach($dataset as $field => $value) { - if ( false === ( $this->entityResolver->searchFieldAnnotation($field, [ Attribute\Property\Field::class, Field::class ], false)->readonly ?? false ) ) { + if ( false === ( $this->entityResolver->searchFieldAnnotation($field, [ Field::class, Field::class ], false)->readonly ?? false ) ) { $intersect[$field] = $field; } } @@ -395,7 +397,7 @@ class Repository $prependField and ($prependField .= "$"); foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { - if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) { + if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) { $this->select(sprintf("%s.$key as {$prependField}{$field->name}", $this->escapeIdentifier($alias))); } } @@ -408,7 +410,7 @@ class Repository $fieldlist = []; foreach ($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { - if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Attribute\Property\Relation\Ignore::class, RelationIgnore::class ])) { + if (null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) { $fieldlist[] = $key; $fieldlist[] = $entity::field($field->name, $this->escapeIdentifier($alias)); } @@ -623,26 +625,26 @@ class Repository $this->joined[$item] = true; } - $annotation = $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Join::class ]) ?: - $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Relation::class ]); + $attribute = $this->entityResolver->searchFieldAnnotation($item, [ Join::class ]) ?: + $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]); - $isRelation = ( $annotation instanceof Relation ) || ($annotation instanceof Attribute\Property\Relation); + $isRelation = ( $attribute instanceof Relation ) || ($attribute instanceof Relation); - if ($isRelation && ( $annotation->isManyToMany() )) { + if ($isRelation && ( $attribute->isManyToMany() )) { throw new \Exception("Many-to-many relation can not be preloaded within joins."); } - if ( $annotation ) { - $alias = $annotation->alias ?? $item; + if ( $attribute ) { + $alias = $attribute->alias ?? $item; - $entity = $annotation->entity ?? $this->entityResolver->reflectedClass->getProperties(true)[$item]->getTypes()[0]->type; + $entity = $attribute->entity ?? $this->entityResolver->reflectedClass->getProperties(true)[$item]->getTypes()[0]->type; foreach($entity::resolveEntity()->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { - if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Attribute\Property\Relation\Ignore::class ]) ) { + if ( null === $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Relation\Ignore::class ]) ) { $escAlias = $this->escapeIdentifier($alias); $fieldName = $this->escapeIdentifier($key); - $name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Attribute\Property\Field::class ])->name ?? $field->name; + $name = $entity::resolveEntity()->searchFieldAnnotation($field->name, [ Field::class ])->name ?? $field->name; $this->select("$escAlias.$fieldName as $alias\${$name}"); @@ -652,7 +654,7 @@ class Repository $this->open(); if ( ! in_array(WithOptionEnum::SkipWhere, $options)) { - foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class ] ) as $condition) { + 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); } @@ -660,26 +662,26 @@ class Repository } if ( ! in_array(WithOptionEnum::SkipHaving, $options)) { - foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class ]) as $condition) { + foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ]) as $condition) { $this->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator); } } if ( ! in_array(WithOptionEnum::SkipFilter, $options)) { - foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Filter::class ]) as $filter) { + foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Filter::class ]) as $filter) { call_user_func_array([$this->entityClass, $filter->method], [$this, $item, true]); } } $this->close(); - $key = is_string($annotation->key) ? $this->entityClass::field($annotation->key) : $annotation->key; + $key = is_string($attribute->key) ? $this->entityClass::field($attribute->key) : $attribute->key; - $foreignKey = is_string($annotation->foreignKey) ? $entity::field($annotation->foreignKey, $alias) : $annotation->foreignKey; + $foreignKey = is_string($attribute->foreignKey) ? $entity::field($attribute->foreignKey, $alias) : $attribute->foreignKey; $this->join("LEFT", $entity::resolveEntity()->tableName(), $key, $foreignKey, $alias, function($join) use ($item, $entity, $alias, $options) { if ( ! in_array(WithOptionEnum::SkipJoinWhere, $options)) { - foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class ]) as $condition) { + foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) { if ( ! is_object($condition->field) ) { $field = $this->entityClass::field($condition->field); } @@ -697,14 +699,14 @@ class Repository } if ( ! in_array(WithOptionEnum::SkipJoinFilter, $options) ) { - foreach ($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\FilterJoin::class ]) as $filter) { + foreach ($this->entityResolver->searchFieldAnnotationList($item, [ FilterJoin::class ]) as $filter) { call_user_func_array([$this->entityClass, $filter->method], [$join, $item, true]); } } }); } else { - throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation annotation."); + throw new \Exception("Referenced field `$item` which do not exist or do not contain a valid @Join or @Relation attribute."); } } @@ -724,7 +726,7 @@ class Repository # Apply FILTER annotation to this too ! foreach(array_filter((array) $fields) as $item) { - if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Attribute\Property\Relation::class ]) ) { + if ( $relation = $this->entityResolver->searchFieldAnnotation($item, [ Relation::class ]) ) { $alias = $relation->alias ?? $item; if ( $relation->isManyToMany() ) { @@ -745,11 +747,11 @@ class Repository # $relation->isManyToMany() and $repository->selectJsonEntity($relation->bridge, $relation->bridgeField, true); - foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Where::class ]) as $condition) { + foreach($this->entityResolver->searchFieldAnnotationList($item, [ Where::class ]) as $condition) { $repository->where(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator); } - foreach($this->entityResolver->searchFieldAnnotationList($item, [ Attribute\Property\Having::class ] ) as $condition) { + foreach($this->entityResolver->searchFieldAnnotationList($item, [ Having::class ] ) as $condition) { $repository->having(is_object($condition->field) ? $condition->field : $entity::field($condition->field), $condition->getValue(), $condition->operator); } @@ -766,7 +768,7 @@ class Repository $this->select("(" . $r = Common\Sql::raw($repository->queryBuilder->render() . ") as $item\$collection")); } else { - throw new \Exception("You referenced field `$item` which do not exist or do not contain a valid @Join annotation."); + throw new \Exception("You referenced field `$item` which do not exist or do not contain a valid @Join attribute."); } } @@ -776,9 +778,9 @@ class Repository public function loadCollectionRelation(EntityCollection $collection, array|string $fields) : void { foreach ((array)$fields as $name) { - if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Attribute\Property\Relation::class ] ))) { - $order = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\OrderBy::class ]); - $where = $this->entityResolver->searchFieldAnnotationList($name, [ Attribute\Property\Where::class ]); + if (null !== ($relation = $this->entityResolver->searchFieldAnnotation($name, [ Relation::class ] ))) { + $order = $this->entityResolver->searchFieldAnnotationList($name, [ OrderBy::class ]); + $where = $this->entityResolver->searchFieldAnnotationList($name, [ Where::class ]); $baseEntity = $relation->entity ?? $relation->bridge ?? $this->entityResolver->properties[$name]['type']; $baseEntityResolver = $baseEntity::resolveEntity(); @@ -789,7 +791,7 @@ class Repository $repository = $baseEntity::repository(); foreach ($baseEntityResolver->fieldList(Common\EntityResolver::KEY_COLUMN_NAME, true) as $key => $field) { - if (null === $baseEntityResolver->searchFieldAnnotation($field->name, [ Attribute\Property\Relation\Ignore::class ])) { + if (null === $baseEntityResolver->searchFieldAnnotation($field->name, [ Relation\Ignore::class ])) { $repository->select($baseEntityResolver->entityClass::field($key)); } } diff --git a/src/Repository/RelationBuilder.php b/src/Repository/RelationBuilder.php index f5b9805..4fa2f1d 100644 --- a/src/Repository/RelationBuilder.php +++ b/src/Repository/RelationBuilder.php @@ -199,20 +199,20 @@ class RelationBuilder protected function fetchFromDataset($name, ? array $data = null) : object|bool { - $annotation = $this->resolver->searchFieldAnnotation($name, [ Join::class ]) ?: + $attribute = $this->resolver->searchFieldAnnotation($name, [ Join::class ]) ?: $this->resolver->searchFieldAnnotation($name, [ Relation::class ]); - if ( $annotation ) { + if ( $attribute ) { $vars = []; $len = strlen( $name ) + 1; - $isRelation = $annotation instanceof Relation; + $isRelation = $attribute instanceof Relation; - if ( $isRelation && $annotation->isManyToMany() ) { - $entity = $this->relationAnnotations($name, $annotation)['relationRelation']->entity; + if ( $isRelation && $attribute->isManyToMany() ) { + $entity = $this->relationAnnotations($name, $attribute)['relationRelation']->entity; } else { - $entity = $annotation->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type; + $entity = $attribute->entity ?? $this->resolver->reflectedClass->getProperties()[$name]->getTypes()[0]->type; } $name = strtolower($name); @@ -323,7 +323,7 @@ class RelationBuilder $relationRelation = $bridgeEntity->searchFieldAnnotation($relation->foreignField, [ Relation::class ]); if ($relationRelation === null) { - throw new \Exception("@Relation annotation not found for field `{$relation->foreignField}` in entity {$relation->bridge}"); + throw new \Exception("@Relation attribute not found for field `{$relation->foreignField}` in entity {$relation->bridge}"); } $relationRelation->entity ??= $relation->bridge::resolveEntity()->reflectedClass->getProperties()[$relation->foreignField]->getTypes()[0]->type; diff --git a/src/SearchRequest/SearchRequestFromRequestTrait.php b/src/SearchRequest/SearchRequestFromRequestTrait.php index c3f2601..3d67862 100644 --- a/src/SearchRequest/SearchRequestFromRequestTrait.php +++ b/src/SearchRequest/SearchRequestFromRequestTrait.php @@ -18,6 +18,10 @@ trait SearchRequestFromRequestTrait public function fromRequest(ServerRequestInterface $request) { + if (method_exists($this, 'prepare')) { + $this->prepare($request); + } + $queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; })); $this->page = $queryParams->offsetExists('page') ? $queryParams['page'] : 1;