Skip to content

Commit 798d0f4

Browse files
authored
refactor(mapper): split caster and serializer into two (#1041)
1 parent 4a00657 commit 798d0f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+952
-194
lines changed

src/Tempest/Database/src/Builder/FieldName.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
use Stringable;
88
use Tempest\Database\DatabaseModel;
9-
use Tempest\Mapper\Casters\CasterFactory;
9+
use Tempest\Mapper\CasterFactory;
1010
use Tempest\Reflection\ClassReflector;
1111

12+
use function Tempest\get;
13+
1214
final class FieldName implements Stringable
1315
{
1416
public function __construct(
@@ -21,7 +23,7 @@ public function __construct(
2123
/** @return \Tempest\Database\Builder\FieldName[] */
2224
public static function make(ClassReflector $class, ?TableName $tableName = null): array
2325
{
24-
$casterFactory = new CasterFactory();
26+
$casterFactory = get(CasterFactory::class);
2527
$fieldNames = [];
2628
$tableName ??= $class->callStatic('table');
2729

src/Tempest/Database/src/Mappers/ModelToQueryMapper.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66

77
use Tempest\Database\DatabaseModel;
88
use Tempest\Database\Query;
9-
use Tempest\Mapper\Casters\CasterFactory;
109
use Tempest\Mapper\Mapper;
10+
use Tempest\Mapper\SerializerFactory;
1111
use Tempest\Reflection\ClassReflector;
1212

1313
use function Tempest\map;
1414

1515
final readonly class ModelToQueryMapper implements Mapper
1616
{
1717
public function __construct(
18-
private CasterFactory $casterFactory,
18+
private SerializerFactory $serializerFactory,
1919
) {
2020
}
2121

@@ -138,9 +138,9 @@ private function fields(DatabaseModel $model): array
138138

139139
$value = $property->getValue($model);
140140

141-
// Check if caster is available for value serialization
142-
if ($value !== null && ($caster = $this->casterFactory->forProperty($property))) {
143-
$value = $caster->serialize($value);
141+
// Check if serializer is available for value serialization
142+
if ($value !== null && ($serializer = $this->serializerFactory->forProperty($property))) {
143+
$value = $serializer->serialize($value);
144144
}
145145

146146
$fields[$property->getName()] = $value;

src/Tempest/Database/src/Mappers/QueryToModelMapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Tempest\Database\DatabaseModel;
88
use Tempest\Database\Query;
9-
use Tempest\Mapper\Casters\CasterFactory;
9+
use Tempest\Mapper\CasterFactory;
1010
use Tempest\Mapper\Mapper;
1111
use Tempest\Reflection\ClassReflector;
1212
use Tempest\Reflection\PropertyReflector;

src/Tempest/Mapper/src/Caster.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,4 @@
77
interface Caster
88
{
99
public function cast(mixed $input): mixed;
10-
11-
public function serialize(mixed $input): array|string;
1210
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tempest\Mapper;
6+
7+
use Closure;
8+
use Tempest\Reflection\PropertyReflector;
9+
10+
use function Tempest\get;
11+
12+
final class CasterFactory
13+
{
14+
/**
15+
* @var array{string|Closure, class-string<\Tempest\Mapper\Caster>|Closure}[]
16+
*/
17+
private array $casters = [];
18+
19+
/**
20+
* @param class-string<\Tempest\Mapper\Caster> $casterClass
21+
*/
22+
public function addCaster(string|Closure $for, string|Closure $casterClass): self
23+
{
24+
$this->casters = [[$for, $casterClass], ...$this->casters];
25+
26+
return $this;
27+
}
28+
29+
public function forProperty(PropertyReflector $property): ?Caster
30+
{
31+
$type = $property->getType();
32+
33+
// Get CastWith from the property
34+
$castWith = $property->getAttribute(CastWith::class);
35+
36+
// Get CastWith from the property's type if there's no property-defined CastWith
37+
if ($castWith === null && $type->isClass()) {
38+
$castWith = $type->asClass()->getAttribute(CastWith::class, recursive: true);
39+
}
40+
41+
// Return the caster if defined with CastWith
42+
if ($castWith !== null) {
43+
// Resolve the caster from the container
44+
return get($castWith->className);
45+
}
46+
47+
// Resolve caster from manual additions
48+
foreach ($this->casters as [$for, $casterClass]) {
49+
if (is_callable($for) && $for($property) || is_string($for) && $type->matches($for) || $type->getName() === $for) {
50+
return is_callable($casterClass)
51+
? $casterClass($property)
52+
: get($casterClass);
53+
}
54+
}
55+
56+
return null;
57+
}
58+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Tempest\Mapper;
4+
5+
use BackedEnum;
6+
use DateTime;
7+
use DateTimeImmutable;
8+
use DateTimeInterface;
9+
use Tempest\Container\Container;
10+
use Tempest\Container\Initializer;
11+
use Tempest\Container\Singleton;
12+
use Tempest\Mapper\Casters\ArrayToObjectCollectionCaster;
13+
use Tempest\Mapper\Casters\DateTimeCaster;
14+
use Tempest\Mapper\Casters\EnumCaster;
15+
use Tempest\Mapper\Casters\JsonToArrayCaster;
16+
use Tempest\Mapper\Casters\ObjectCaster;
17+
use Tempest\Reflection\PropertyReflector;
18+
19+
final class CasterFactoryInitializer implements Initializer
20+
{
21+
#[Singleton]
22+
public function initialize(Container $container): CasterFactory
23+
{
24+
return new CasterFactory()
25+
->addCaster('array', JsonToArrayCaster::class)
26+
->addCaster(fn (PropertyReflector $property) => $property->getIterableType() !== null, fn (PropertyReflector $property) => new ArrayToObjectCollectionCaster($property))
27+
->addCaster(fn (PropertyReflector $property) => $property->getType()->isClass(), fn (PropertyReflector $property) => new ObjectCaster($property->getType()))
28+
->addCaster(BackedEnum::class, fn (PropertyReflector $property) => new EnumCaster($property->getType()->getName()))
29+
->addCaster(DateTimeImmutable::class, DateTimeCaster::fromProperty(...))
30+
->addCaster(DateTime::class, DateTimeCaster::fromProperty(...))
31+
->addCaster(DateTimeInterface::class, DateTimeCaster::fromProperty(...));
32+
}
33+
}

src/Tempest/Mapper/src/Casters/ArrayJsonCaster.php

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/Tempest/Mapper/src/Casters/ArrayObjectCaster.php renamed to src/Tempest/Mapper/src/Casters/ArrayToObjectCollectionCaster.php

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use function Tempest\map;
1212

13-
final readonly class ArrayObjectCaster implements Caster
13+
final readonly class ArrayToObjectCollectionCaster implements Caster
1414
{
1515
public function __construct(
1616
private PropertyReflector $property,
@@ -29,15 +29,4 @@ public function cast(mixed $input): mixed
2929

3030
return $values;
3131
}
32-
33-
public function serialize(mixed $input): array
34-
{
35-
$values = [];
36-
37-
foreach ($input as $key => $item) {
38-
$values[$key] = map($item)->with(ObjectToArrayMapper::class)->do();
39-
}
40-
41-
return $values;
42-
}
4332
}

src/Tempest/Mapper/src/Casters/BooleanCaster.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,11 @@
55
namespace Tempest\Mapper\Casters;
66

77
use Tempest\Mapper\Caster;
8-
use Tempest\Mapper\Exceptions\CannotSerializeValue;
98

109
final readonly class BooleanCaster implements Caster
1110
{
1211
public function cast(mixed $input): bool
1312
{
1413
return boolval($input);
1514
}
16-
17-
public function serialize(mixed $input): string
18-
{
19-
if (! is_bool($input)) {
20-
throw new CannotSerializeValue('boolean');
21-
}
22-
23-
return $input ? 'true' : 'false';
24-
}
2515
}

src/Tempest/Mapper/src/Casters/CasterFactory.php

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)