implement FindById, move to proper type alanysis on repository return types

master
Josha von Gizycki 1 week ago
parent 79c0272cec
commit 7783b245df

@ -15,6 +15,7 @@ use Kartierung\Repository\ResolutionStrategy\ResolutionStrategyFactory;
use ReflectionClass; use ReflectionClass;
use ReflectionIntersectionType; use ReflectionIntersectionType;
use ReflectionMethod; use ReflectionMethod;
use ReflectionNamedType;
use ReflectionUnionType; use ReflectionUnionType;
/** /**
@ -54,23 +55,25 @@ readonly class RepositoryAnalyzer
$methods = []; $methods = [];
foreach ($refMethods as $refMethod) { foreach ($refMethods as $refMethod) {
$query = $this->queryString($refMethod); $query = $this->queryString($refMethod);
$returnType = $this->returnType($refMethod); list($returnType, $returnsNullable) = $this->returnType($refMethod);
$listType = $this->listType($refMethod); $listType = $this->listType($refMethod);
$params = $this->parameters($refMethod); $params = $this->parameters($refMethod);
$name = $refMethod->name; $name = $refMethod->name;
$resolutionStrategy = $this->methodValidator->forRepositoryMethod( $resolutionStrategy = $this->methodValidator->forRepositoryMethod(
$refClass->name, repName: $refClass->name,
$name, methodName: $name,
$query, query: $query,
$returnType, returnType: $returnType,
$listType, returnsNullable: $returnsNullable,
$params listType: $listType,
params: $params
); );
$methods[$refMethod->name] = new RepositoryMethod( $methods[$refMethod->name] = new RepositoryMethod(
query: $query, query: $query,
returnType: $returnType, returnType: $returnType,
returnsNullable: $returnsNullable,
returnListType: $listType, returnListType: $listType,
name: $name, name: $name,
parameters: $params, parameters: $params,
@ -101,9 +104,9 @@ readonly class RepositoryAnalyzer
/** /**
* @param ReflectionMethod $refMethod * @param ReflectionMethod $refMethod
* @return class-string<object>|string * @return array{class-string<object>|string, bool}
*/ */
private function returnType(ReflectionMethod $refMethod): string private function returnType(ReflectionMethod $refMethod): array
{ {
$returnType = $refMethod->getReturnType(); $returnType = $refMethod->getReturnType();
@ -116,8 +119,11 @@ readonly class RepositoryAnalyzer
); );
} }
/** @var class-string<object>|string */ /** @var ReflectionNamedType $returnType */
return $returnType->__toString();
/** @var class-string<object>|string $typeStr */
$typeStr = $returnType->getName();
return [$typeStr, $returnType->allowsNull()];
} }
/** /**

@ -6,6 +6,7 @@ namespace Kartierung\Analyze\Result;
use Kartierung\Repository\ResolutionStrategy\ResolutionStrategy; use Kartierung\Repository\ResolutionStrategy\ResolutionStrategy;
use Kartierung\Sql\LazyQuery; use Kartierung\Sql\LazyQuery;
use phpDocumentor\Reflection\Types\Boolean;
readonly class RepositoryMethod readonly class RepositoryMethod
{ {
@ -16,17 +17,13 @@ readonly class RepositoryMethod
public function __construct( public function __construct(
public ?string $query, public ?string $query,
public string $returnType, public string $returnType,
public bool $returnsNullable,
public ?string $returnListType, public ?string $returnListType,
public string $name, public string $name,
public array $parameters, public array $parameters,
public ResolutionStrategy $resolutionStrategy public ResolutionStrategy $resolutionStrategy
) {} ) {}
public function returnsNullable(): bool
{
return str_starts_with($this->returnType, '?');
}
/** /**
* @param array<string, mixed> $parameters * @param array<string, mixed> $parameters
*/ */

@ -4,17 +4,51 @@ declare(strict_types=1);
namespace Kartierung\Repository\ResolutionStrategy; namespace Kartierung\Repository\ResolutionStrategy;
use Kartierung\Analyze\EntityAnalyzer;
use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\RepositoryMethod; use Kartierung\Analyze\Result\RepositoryMethod;
use Kartierung\Sql\DataToEntityMapper;
use Kartierung\Sql\LazyQuery; use Kartierung\Sql\LazyQuery;
use PDO; use PDO;
use PDOStatement;
class FindById implements ResolutionStrategy readonly class FindById implements ResolutionStrategy
{ {
public function createQuery(RepositoryMethod $method, array $parameters): LazyQuery public function createQuery(RepositoryMethod $method, array $parameters): LazyQuery
{ {
return new LazyQuery( return new LazyQuery(
function (PDO $pdo) { static function (PDO $pdo) use ($method, $parameters) {
/** @var class-string<object> $type */
$type = $method->returnType;
$entityResult = (new EntityAnalyzer($type))->analyze();
$mapper = new DataToEntityMapper($entityResult);
$fields = implode(
', ',
array_map(
static fn(EntityField $e) => $e->columnName, $entityResult->fields
)
);
$idField = $entityResult->idField->name;
$sql = "
SELECT $fields
FROM {$entityResult->tableName}
WHERE $idField = ?
";
/** @var PDOStatement $stmt */
$stmt = $pdo->prepare($sql);
$stmt->execute([array_first($parameters)]);
/** @var array<string, mixed>|false $row */
$row = $stmt->fetch(mode: PDO::FETCH_ASSOC);
if($row) {
return $mapper->toEntity($row);
}
return null;
} }
); );
} }

@ -17,12 +17,12 @@ class ResolutionStrategyFactory
string $methodName, string $methodName,
?string $query, ?string $query,
string $returnType, string $returnType,
bool $returnsNullable,
?string $listType, ?string $listType,
array $params array $params
): ResolutionStrategy ): ResolutionStrategy
{ {
$oneParam = count($params) === 1; $oneParam = count($params) === 1;
$returnsNullable = str_starts_with($returnType, '?');
return match (true) { return match (true) {
$query !== null => new UserDefinedQuery(), $query !== null => new UserDefinedQuery(),
@ -40,7 +40,7 @@ class ResolutionStrategyFactory
$methodName === 'findById' && $oneParam && $returnsNullable => new FindById(), $methodName === 'findById' && $oneParam && $returnsNullable => new FindById(),
$methodName === 'findById' => throw new InvalidRepository( $methodName === 'findById' => throw new InvalidRepository(
"fnyById functions expect exactly one parameter and nullable return type." . "fnyById functions expect exactly one parameter and nullable return type. " .
"Invalid function found in $repName" "Invalid function found in $repName"
), ),

@ -130,7 +130,7 @@ class RepositoryAnalyzerTest extends TestCase
// Then // Then
$method = $result->methods['findById']; $method = $result->methods['findById'];
$this->assertEquals('?Kartierung\FullEntity', $method->returnType); $this->assertEquals('Kartierung\FullEntity', $method->returnType);
$this->assertTrue($method->returnsNullable()); $this->assertTrue($method->returnsNullable);
} }
} }

@ -34,6 +34,7 @@ class FindAllTest extends SqliteBaseCase
$method = new RepositoryMethod( $method = new RepositoryMethod(
query: '', query: '',
returnType: '', returnType: '',
returnsNullable: true,
returnListType: SimpleEntity::class, returnListType: SimpleEntity::class,
name: '', name: '',
parameters: [], parameters: [],

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace Kartierung\Repository\ResolutionStrategy;
use Kartierung\Analyze\Result\RepositoryMethod;
use PHPUnit\Framework\Attributes\Group;
class FindByIdTest extends SqliteBaseCase
{
protected function setUp(): void
{
parent::setUp();
$this->getPdo()->exec(
"
CREATE TABLE SimpleEntity (
stringcol VARCHAR(250),
intcol INT
);
INSERT INTO SimpleEntity(stringcol, intcol)
VALUES ('dings', 1),
('bumms', 2),
('foobar', 3);
"
);
}
#[Group('integration')]
public function testSelectsFirstEntry(): void
{
// Given
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
returnsNullable: true,
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
);
$query = $method->createQuery([1]);
// When
$result = $query->execute($this->getPdo());
// Then
$this->assertEquals(
new SimpleEntity(1, 'dings'),
$result
);
}
#[Group('integration')]
public function testSelectsThirdEntry(): void
{
// Given
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
returnsNullable: true,
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
);
$query = $method->createQuery([3]);
// When
$result = $query->execute($this->getPdo());
// Then
$this->assertEquals(
new SimpleEntity(3, 'foobar'),
$result
);
}
#[Group('integration')]
public function testSelectsNothingMatching(): void
{
// Given
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
returnsNullable: true,
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
);
$query = $method->createQuery([4]);
// When
$result = $query->execute($this->getPdo());
// Then
$this->assertNull($result);
}
}

@ -13,11 +13,11 @@ class ResolutionStrategyFactoryTest extends TestCase
public static function validFunctionsProvider(): array public static function validFunctionsProvider(): array
{ {
return [ return [
['R', 'save', null, '', null, [1], SaveEntity::class], ['R', 'save', null, '', false, null, [1], SaveEntity::class],
['R', 'delete', null, 'int', null, [1], DeleteEntity::class], ['R', 'delete', null, 'int', false, null, [1], DeleteEntity::class],
['R', 'findById', null, '?int', null, [1], FindById::class], ['R', 'findById', null, 'int', true, null, [1], FindById::class],
['R', 'findAll', null, 'array', FullEntity::class, [], FindAll::class], ['R', 'findAll', null, 'array', false, FullEntity::class, [], FindAll::class],
['R', 'something', 'SELECT', 'int', null, [], UserDefinedQuery::class] ['R', 'something', 'SELECT', 'int', false, null, [], UserDefinedQuery::class]
]; ];
} }
@ -27,6 +27,7 @@ class ResolutionStrategyFactoryTest extends TestCase
string $name, string $name,
?string $query, ?string $query,
string $returnType, string $returnType,
bool $returnsNullable,
?string $listType, ?string $listType,
array $params, array $params,
string $strategyClass string $strategyClass
@ -38,6 +39,7 @@ class ResolutionStrategyFactoryTest extends TestCase
methodName: $name, methodName: $name,
query: $query, query: $query,
returnType: $returnType, returnType: $returnType,
returnsNullable: $returnsNullable,
listType: $listType, listType: $listType,
params: $params params: $params
); );
@ -69,12 +71,13 @@ class ResolutionStrategyFactoryTest extends TestCase
{ {
// When // When
$function = (new ResolutionStrategyFactory())->forRepositoryMethod( $function = (new ResolutionStrategyFactory())->forRepositoryMethod(
$repName, repName: $repName,
$name, methodName: $name,
$query, query: $query,
$returnType, returnType: $returnType,
$listType, returnsNullable: false,
$params listType: $listType,
params: $params
); );
// Then // Then

@ -13,12 +13,4 @@ class SimpleEntity
public int $intcol, public int $intcol,
public string $stringcol public string $stringcol
) {} ) {}
public function withStringCol(string $value): self
{
$new = new self();
$new->stringcol = $value;
return $new;
}
} }

Loading…
Cancel
Save