first draft for child entities

recursion check missing
master
Josha von Gizycki 2 weeks ago
parent b10dfdf411
commit 6537a2db81

@ -5,20 +5,19 @@ declare(strict_types=1);
namespace Kartierung\Analyze; namespace Kartierung\Analyze;
use Kartierung\Analyze\Exception\InvalidEntity; use Kartierung\Analyze\Exception\InvalidEntity;
use Kartierung\Analyze\Exception\InvalidRepository;
use Kartierung\Analyze\Result\ClassType;
use Kartierung\Analyze\Result\EntityField; use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\EntityResult; use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\FieldAccess; use Kartierung\Analyze\Result\FieldAccess;
use Kartierung\Analyze\Result\PrimitiveType; use Kartierung\Analyze\Result\Type\ArrayType;
use Kartierung\Analyze\Result\Type\ClassType;
use Kartierung\Analyze\Result\Type\PrimitiveType;
use Kartierung\Attribute\Column; use Kartierung\Attribute\Column;
use Kartierung\Attribute\Id; use Kartierung\Attribute\Id;
use Kartierung\Attribute\ListOf;
use Kartierung\Attribute\Table; use Kartierung\Attribute\Table;
use ReflectionClass; use ReflectionClass;
use ReflectionIntersectionType;
use ReflectionNamedType; use ReflectionNamedType;
use ReflectionParameter; use ReflectionParameter;
use ReflectionUnionType;
/** /**
* @template E of object * @template E of object
@ -34,6 +33,7 @@ readonly class EntityAnalyzer
/** /**
* @return EntityResult<E> * @return EntityResult<E>
* @noinspection PhpDocMissingThrowsInspection
*/ */
public function analyze(): EntityResult public function analyze(): EntityResult
{ {
@ -102,9 +102,6 @@ readonly class EntityAnalyzer
return $idFields[array_key_first($idFields)]; return $idFields[array_key_first($idFields)];
} }
/**
* @return EntityField
*/
private function fieldFromParameter(ReflectionParameter $param): EntityField private function fieldFromParameter(ReflectionParameter $param): EntityField
{ {
$attrs = $param->getAttributes(Column::class); $attrs = $param->getAttributes(Column::class);
@ -130,7 +127,7 @@ readonly class EntityAnalyzer
); );
} }
private function parameterType(ReflectionParameter $param): PrimitiveType|ClassType private function parameterType(ReflectionParameter $param): PrimitiveType|ClassType|ArrayType
{ {
if (!$param->hasType()) { if (!$param->hasType()) {
throw new InvalidEntity( throw new InvalidEntity(
@ -140,9 +137,9 @@ readonly class EntityAnalyzer
$type = $param->getType(); $type = $param->getType();
if (!$type instanceof ReflectionNamedType) { if (!$type instanceof ReflectionNamedType) {
throw new InvalidRepository( throw new InvalidEntity(
"Entity parameter $this->entity::$param->name has an invalid return type. " "Entity parameter $this->entity::$param->name has an invalid return type. "
. 'Simple type expected. Unions, Intersections or no type are not simple.' . "Simple type expected. Unions, Intersections or no type are not simple."
); );
} }
@ -150,6 +147,23 @@ readonly class EntityAnalyzer
/** @var class-string<object> $classType */ /** @var class-string<object> $classType */
$classType = $type->getName(); $classType = $type->getName();
if ($classType === "array") {
$listAttr = $param->getAttributes(ListOf::class);
if (empty($listAttr) || count($listAttr) > 1) {
throw new InvalidEntity(
"Entity parameter $this->entity::$param->name declares its typr as array. "
. "Exactly one #[ListOf(...)] attribute expected."
);
}
/** @var ListOf $listAttrInstance */
$listAttrInstance = $listAttr[0]->newInstance();
return new ArrayType(
(new EntityAnalyzer($listAttrInstance->entitiyFqcn))->analyze()
);
}
return match ($type->getName()) { return match ($type->getName()) {
"string" => PrimitiveType::STRING, "string" => PrimitiveType::STRING,
"bool" => PrimitiveType::BOOLEAN, "bool" => PrimitiveType::BOOLEAN,
@ -161,7 +175,7 @@ readonly class EntityAnalyzer
/** /**
* @param ReflectionClass<E> $refClass * @param ReflectionClass<E> $refClass
* @return FieldAccess * @noinspection PhpDocMissingThrowsInspection
*/ */
private function readAccess(ReflectionClass $refClass, ReflectionParameter $param): FieldAccess private function readAccess(ReflectionClass $refClass, ReflectionParameter $param): FieldAccess
{ {

@ -9,7 +9,7 @@ use Kartierung\Analyze\Result\ParameterFunction;
use Kartierung\Analyze\Result\RepositoryMethod; use Kartierung\Analyze\Result\RepositoryMethod;
use Kartierung\Analyze\Result\RepositoryMethodParameter; use Kartierung\Analyze\Result\RepositoryMethodParameter;
use Kartierung\Analyze\Result\RepositoryResult; use Kartierung\Analyze\Result\RepositoryResult;
use Kartierung\Attribute\ListResultOf; use Kartierung\Attribute\ListOf;
use Kartierung\Attribute\Query; use Kartierung\Attribute\Query;
use Kartierung\Repository\ResolutionStrategy\ResolutionStrategyFactory; use Kartierung\Repository\ResolutionStrategy\ResolutionStrategyFactory;
use ReflectionClass; use ReflectionClass;
@ -126,11 +126,11 @@ readonly class RepositoryAnalyzer
*/ */
private function listType(ReflectionMethod $refMethod): ?string private function listType(ReflectionMethod $refMethod): ?string
{ {
$listResultOfAttr = $refMethod->getAttributes(ListResultOf::class); $listResultOfAttr = $refMethod->getAttributes(ListOf::class);
$listType = null; $listType = null;
if (count($listResultOfAttr) === 1) { if (count($listResultOfAttr) === 1) {
/** @var ListResultOf $inst */ /** @var ListOf $inst */
$inst = $listResultOfAttr[0]->newInstance(); $inst = $listResultOfAttr[0]->newInstance();
$listType = $inst->entitiyFqcn; $listType = $inst->entitiyFqcn;

@ -4,11 +4,15 @@ declare(strict_types=1);
namespace Kartierung\Analyze\Result; namespace Kartierung\Analyze\Result;
use Kartierung\Analyze\Result\Type\ArrayType;
use Kartierung\Analyze\Result\Type\ClassType;
use Kartierung\Analyze\Result\Type\PrimitiveType;
readonly class EntityField readonly class EntityField
{ {
public function __construct( public function __construct(
public string $name, public string $name,
public PrimitiveType|ClassType $type, public PrimitiveType|ClassType|ArrayType $type,
public string $columnName, public string $columnName,
public bool $isIdField, public bool $isIdField,
public FieldAccess $readAccess, public FieldAccess $readAccess,

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Kartierung\Analyze\Result\Type;
use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Attribute\ListOf;
readonly class ArrayType
{
/**
* @param EntityResult<object> $entity
*/
public function __construct(
public EntityResult $entity
) {}
}

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Kartierung\Analyze\Result; namespace Kartierung\Analyze\Result\Type;
readonly class ClassType readonly class ClassType
{ {

@ -2,7 +2,7 @@
declare(strict_types=1); declare(strict_types=1);
namespace Kartierung\Analyze\Result; namespace Kartierung\Analyze\Result\Type;
enum PrimitiveType enum PrimitiveType
{ {

@ -6,8 +6,8 @@ namespace Kartierung\Attribute;
use Attribute; use Attribute;
#[Attribute(Attribute::TARGET_METHOD)] #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
readonly class ListResultOf readonly class ListOf
{ {
/** /**
* @param class-string $entitiyFqcn * @param class-string $entitiyFqcn

@ -6,8 +6,10 @@ namespace Kartierung\Analyze;
use Kartierung\Analyze\Exception\InvalidEntity; use Kartierung\Analyze\Exception\InvalidEntity;
use Kartierung\Analyze\Result\EntityField; use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\FieldAccess; use Kartierung\Analyze\Result\FieldAccess;
use Kartierung\Analyze\Result\PrimitiveType; use Kartierung\Analyze\Result\Type\ArrayType;
use Kartierung\Analyze\Result\Type\PrimitiveType;
use Kartierung\FullEntity; use Kartierung\FullEntity;
use Kartierung\PropertyPropagatedEntity; use Kartierung\PropertyPropagatedEntity;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -114,4 +116,56 @@ class EntityAnalyzerTest extends TestCase
// Then // Then
$this->assertEquals("EntityWithoutTableAttribute", $result->tableName); $this->assertEquals("EntityWithoutTableAttribute", $result->tableName);
} }
public function testArrayOfChildEntities(): void
{
// Given
$entity = EntityWithListOfFullEntities::class;
// When
$result = (new EntityAnalyzer($entity))->analyze();
// Then
$this->assertEquals(
new EntityField(
name: 'id',
type: PrimitiveType::INTEGER,
columnName: 'id',
isIdField: true,
readAccess: FieldAccess::PUBLIC
),
$result->fields[0]
);
$this->assertEquals(
new EntityField(
name: 'children',
type: new ArrayType(
new EntityResult(
classFqcn: IdOnlyEntity::class,
tableName: 'IdOnlyEntity',
fields: [
new EntityField(
name: 'id',
type: PrimitiveType::INTEGER,
columnName: 'id',
isIdField: true,
readAccess: FieldAccess::PUBLIC
)
],
idField: new EntityField(
name: 'id',
type: PrimitiveType::INTEGER,
columnName: 'id',
isIdField: true,
readAccess: FieldAccess::PUBLIC
)
)
),
columnName: 'children',
isIdField: false,
readAccess: FieldAccess::PUBLIC
),
$result->fields[1]
);
}
} }

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Kartierung\Analyze;
use Kartierung\Attribute\Id;
use Kartierung\Attribute\ListOf;
class EntityWithListOfFullEntities
{
public function __construct(
#[Id]
public int $id,
#[ListOf(IdOnlyEntity::class)]
public array $children
) {}
}

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Kartierung\Analyze;
use Kartierung\Attribute\Id;
class IdOnlyEntity
{
public function __construct(
#[Id]
public int $id
) {}
}

@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Kartierung\Analyze; namespace Kartierung\Analyze;
use Kartierung\Attribute\ListResultOf; use Kartierung\Attribute\ListOf;
interface InvalidListResultOfRepository interface InvalidListResultOfRepository
{ {
#[ListResultOf('void')] #[ListOf('void')]
public function invalidListResultOf(): array; public function invalidListResultOf(): array;
} }

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Kartierung\Analyze; namespace Kartierung\Analyze;
use Kartierung\Attribute\ListResultOf; use Kartierung\Attribute\ListOf;
use Kartierung\Attribute\Query; use Kartierung\Attribute\Query;
use Kartierung\FullEntity; use Kartierung\FullEntity;
@ -14,7 +14,7 @@ interface Repository
public function fetchWithQuery(): FullEntity; public function fetchWithQuery(): FullEntity;
#[Query("SELECT something")] #[Query("SELECT something")]
#[ListResultOf(FullEntity::class)] #[ListOf(FullEntity::class)]
public function fetchArray(): array; public function fetchArray(): array;
#[Query("SELECT something")] #[Query("SELECT something")]

@ -7,7 +7,7 @@ namespace Kartierung\Sql;
use Kartierung\Analyze\Result\EntityField; use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\EntityResult; use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\FieldAccess; use Kartierung\Analyze\Result\FieldAccess;
use Kartierung\Analyze\Result\PrimitiveType; use Kartierung\Analyze\Result\Type\PrimitiveType;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class DataToEntityMapperTest extends TestCase class DataToEntityMapperTest extends TestCase

Loading…
Cancel
Save