preparations for SaveEntity

master
Josha von Gizycki 1 week ago
parent 7783b245df
commit c0dd69eac0

@ -171,7 +171,7 @@ readonly class EntityAnalyzer
"bool" => PrimitiveType::BOOLEAN,
"int" => PrimitiveType::INTEGER,
"float" => PrimitiveType::FLOAT,
default => new ClassType($classType)
default => new ClassType((new EntityAnalyzer($classType))->analyze())
};
}

@ -161,9 +161,7 @@ readonly class RepositoryAnalyzer
foreach ($refMethod->getParameters() as $param) {
$type = $param->getType();
if ($type instanceof ReflectionUnionType
|| $type instanceof ReflectionIntersectionType
|| $type === null) {
if (!$type instanceof ReflectionNamedType) {
throw new InvalidRepository(
"Type of $refMethod->class::$refMethod->name::$param->name has an invalid type. "
. 'Simple type expected. Unions, Intersections or no type are not simple.'
@ -172,7 +170,7 @@ readonly class RepositoryAnalyzer
$params[] = new RepositoryMethodParameter(
name: $param->getName(),
type: $type->__toString(),
type: $type->getName(),
function: ParameterFunction::STATEMENT_PARAMETER
);
}

@ -6,7 +6,6 @@ namespace Kartierung\Analyze\Result;
use Kartierung\Repository\ResolutionStrategy\ResolutionStrategy;
use Kartierung\Sql\LazyQuery;
use phpDocumentor\Reflection\Types\Boolean;
readonly class RepositoryMethod
{

@ -6,6 +6,11 @@ namespace Kartierung\Analyze\Result;
readonly class RepositoryMethodParameter
{
/**
* @param string $name
* @param string|class-string<object> $type
* @param ParameterFunction $function
*/
public function __construct(
public string $name,
public string $type,

@ -4,12 +4,14 @@ declare(strict_types=1);
namespace Kartierung\Analyze\Result\Type;
use Kartierung\Analyze\Result\EntityResult;
readonly class ClassType
{
/**
* @param class-string<object> $fqcn
* @param EntityResult<object> $entity
*/
public function __construct(
public string $fqcn
public EntityResult $entity
) {}
}

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Kartierung\Repository;
use RuntimeException;
class InvalidCall extends RuntimeException
{
public function __construct(string $message = "")
{
parent::__construct($message);
}
}

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Kartierung\Repository\ResolutionStrategy;
use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\FieldAccess;
use Kartierung\Analyze\Result\Type\ArrayType;
use Kartierung\Analyze\Result\Type\ClassType;
class EntityToDataMapper
{
/**
* @template E of object
* @param EntityResult<E> $entityResult
* @param E $entity
* @return array<string, mixed>
*/
public function byColumnName(EntityResult $entityResult, object $entity): array
{
$result = [];
foreach ($entityResult->fields as $field) {
$type = $field->type;
$value = $this->fieldFromEntity($field, $entity);
if ($type instanceof ArrayType) {
/** @var array<object> $value */
$result[$field->columnName] = array_map(
fn(object $listValue) => $this->byColumnName($type->entity, $listValue),
$value
);
} elseif ($type instanceof ClassType) {
/** @var object $value */
$result[$field->columnName] = $this->byColumnName($type->entity, $value);
} else {
$result[$field->columnName] = $value;
}
}
return $result;
}
private function fieldFromEntity(EntityField $field, object $entity): mixed
{
return match ($field->readAccess) {
FieldAccess::PUBLIC => $entity->{$field->name},
FieldAccess::GETTER => $entity->{'get' . ucfirst($field->name)}()
};
}
}

@ -4,7 +4,11 @@ declare(strict_types=1);
namespace Kartierung\Repository\ResolutionStrategy;
use Kartierung\Analyze\EntityAnalyzer;
use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\RepositoryMethod;
use Kartierung\Analyze\Result\RepositoryMethodParameter;
use Kartierung\Repository\InvalidCall;
use Kartierung\Sql\LazyQuery;
use PDO;
@ -13,9 +17,54 @@ class SaveEntity implements ResolutionStrategy
public function createQuery(RepositoryMethod $method, array $parameters): LazyQuery
{
return new LazyQuery(
function (PDO $pdo) {
function (PDO $pdo) use ($method, $parameters) {
/** @var RepositoryMethodParameter $firstDeclaredParam */
$firstDeclaredParam = array_first($method->parameters);
/** @var object $firstParam */
$firstParam = array_first($parameters);
if (!is_a($firstParam, $firstDeclaredParam->type)) {
throw new InvalidCall(
"Repository method $method->name is expected to be called with an entity " .
"of type $firstDeclaredParam->type. Something else was given."
);
}
/** @var object $firstParam */
/** @var class-string<object> $declaredType */
$declaredType = $firstDeclaredParam->type;
$entityResult = (new EntityAnalyzer($declaredType))->analyze();
$entityData = (new EntityToDataMapper())->byColumnName($entityResult, $firstParam);
if ($this->hasIdValue($entityResult, $entityData)) {
return $this->update();
}
return $this->insert();
}
);
}
private function update(): int
{
return 0;
}
private function insert(): object
{
return new \stdClass();
}
/**
* @template E of object
* @param EntityResult<E> $entityResult
* @param array<mixed> $values
* @return bool
* @noinspection PhpPluralMixedCanBeReplacedWithArrayInspection
*/
private function hasIdValue(EntityResult $entityResult, array $values): bool
{
return ($values[$entityResult->idField->columnName] ?? null) !== null;
}
}

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Kartierung\Repository\ResolutionStrategy;
use Kartierung\Analyze\Result\EntityField;
use Kartierung\Analyze\Result\EntityResult;
use Kartierung\Analyze\Result\FieldAccess;
use Kartierung\Analyze\Result\Type\PrimitiveType;
use PHPUnit\Framework\TestCase;
class EntityToDataMapperTest extends TestCase
{
public function testMapsByColumnName(): void
{
// Given
$entity = new SimpleEntity(1, "2");
$entityResult = new EntityResult(
classFqcn: '',
tableName: '',
fields: [
new EntityField(
name: 'intcol',
type: PrimitiveType::INTEGER,
columnName: 'intcol',
isIdField: true,
readAccess: FieldAccess::PUBLIC
),
new EntityField(
name: 'stringcol',
type: PrimitiveType::INTEGER,
columnName: 'stringcol',
isIdField: true,
readAccess: FieldAccess::PUBLIC
)
],
idField: new EntityField(
name: '',
type: PrimitiveType::INTEGER,
columnName: '',
isIdField: true,
readAccess: FieldAccess::PUBLIC
)
);
// When
$result = (new EntityToDataMapper())->byColumnName(
$entityResult, $entity
);
// Then
$this->assertEquals(
[
'intcol' => 1,
'stringcol' => 2
],
$result
);
}
}

@ -31,6 +31,7 @@ class FindByIdTest extends SqliteBaseCase
public function testSelectsFirstEntry(): void
{
// Given
$findBy = new FindById();
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
@ -38,7 +39,7 @@ class FindByIdTest extends SqliteBaseCase
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
resolutionStrategy: $findBy
);
$query = $method->createQuery([1]);
@ -56,6 +57,7 @@ class FindByIdTest extends SqliteBaseCase
public function testSelectsThirdEntry(): void
{
// Given
$findBy = new FindById();
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
@ -63,7 +65,7 @@ class FindByIdTest extends SqliteBaseCase
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
resolutionStrategy: $findBy
);
$query = $method->createQuery([3]);
@ -81,6 +83,7 @@ class FindByIdTest extends SqliteBaseCase
public function testSelectsNothingMatching(): void
{
// Given
$findBy = new FindById();
$method = new RepositoryMethod(
query: '',
returnType: SimpleEntity::class,
@ -88,7 +91,7 @@ class FindByIdTest extends SqliteBaseCase
returnListType: '',
name: '',
parameters: [],
resolutionStrategy: new FindById()
resolutionStrategy: $findBy
);
$query = $method->createQuery([4]);

@ -11,13 +11,19 @@ use PHPUnit\Framework\TestCase;
abstract class SqliteBaseCase extends TestCase
{
private PDO $pdo;
private $sqlitePath;
protected function setUp(): void
{
parent::setUp();
$tmpfile = tempnam(sys_get_temp_dir(), 'kartierung.sqlite');
$this->pdo = new \PDO("sqlite:/$tmpfile");
$this->sqlitePath = tempnam(sys_get_temp_dir(), 'kartierung.sqlite');
$this->pdo = new \PDO("sqlite:/$this->sqlitePath");
}
protected function tearDown(): void
{
unlink($this->sqlitePath);
}
protected function getPdo(): PDO

Loading…
Cancel
Save