Skip to content

Commit 122d7a3

Browse files
authored
feat(mapper): support mapping array of serialized enums (#1521)
1 parent 5c2a500 commit 122d7a3

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

packages/database/src/Mappers/SelectModelMapper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function map(mixed $from, mixed $to): array
3131
$idField = $model->getQualifiedPrimaryKey();
3232

3333
$parsed = arr($from)
34-
->groupBy(fn (array $data, int $i) => $idField !== null ? ($data[$idField] ?? $i) : $i)
34+
->groupBy(fn (array $data, int|string $i) => $idField !== null ? ($data[$idField] ?? $i) : $i)
3535
->map(fn (array $rows) => $this->normalizeFields($model, $rows))
3636
->values();
3737

packages/mapper/src/Casters/ArrayToObjectCollectionCaster.php

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

77
use Tempest\Mapper\Caster;
8-
use Tempest\Mapper\Mappers\ObjectToArrayMapper;
98
use Tempest\Reflection\PropertyReflector;
9+
use Tempest\Support\Json;
1010

1111
final readonly class ArrayToObjectCollectionCaster implements Caster
1212
{
@@ -18,13 +18,20 @@ public function cast(mixed $input): mixed
1818
{
1919
$values = [];
2020
$iterableType = $this->property->getIterableType();
21-
$objectCaster = new ObjectCaster($iterableType);
21+
22+
$caster = $iterableType->isEnum()
23+
? new EnumCaster($iterableType->getName())
24+
: new ObjectCaster($iterableType);
25+
26+
if (Json\is_valid($input)) {
27+
$input = Json\decode($input);
28+
}
2229

2330
foreach ($input as $key => $item) {
2431
if (is_object($item) && $iterableType->matches($item::class)) {
2532
$values[$key] = $item;
2633
} else {
27-
$values[$key] = $objectCaster->cast($item);
34+
$values[$key] = $caster->cast($item);
2835
}
2936
}
3037

tests/Integration/Database/Mappers/SelectModelMapperTest.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
namespace Integration\Database\Mappers;
3+
namespace Tests\Tempest\Integration\Database\Mappers;
44

55
use Tempest\Auth\Install\User;
66
use Tempest\Database\Mappers\SelectModelMapper;
@@ -121,6 +121,17 @@ public function test_map_user_permissions(): void
121121
$this->assertCount(1, $users[0]->userPermissions);
122122
}
123123

124+
public function test_array_of_serialized_enums(): void
125+
{
126+
$users = map([['id' => 1, 'roles' => json_encode(['admin', 'user'])]])
127+
->with(SelectModelMapper::class)
128+
->to(ObjectWithArrayEnumProperty::class);
129+
130+
$this->assertCount(2, $users[0]->roles);
131+
$this->assertSame(EnumToBeMappedToArray::ADMIN, $users[0]->roles[0]);
132+
$this->assertSame(EnumToBeMappedToArray::USER, $users[0]->roles[1]);
133+
}
134+
124135
private function data(): array
125136
{
126137
return [
@@ -257,3 +268,15 @@ private function data(): array
257268
];
258269
}
259270
}
271+
272+
final class ObjectWithArrayEnumProperty
273+
{
274+
/** @var \Tests\Tempest\Integration\Database\Mappers\EnumToBeMappedToArray[] */
275+
public array $roles;
276+
}
277+
278+
enum EnumToBeMappedToArray: string
279+
{
280+
case ADMIN = 'admin';
281+
case USER = 'user';
282+
}

tests/Integration/Mapper/Mappers/ArrayToObjectMapperTestCase.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,33 @@ public function test_non_strict_values_with_magic_getter(): void
149149

150150
$this->assertSame('magic', $object->a);
151151
}
152+
153+
public function test_map_array_of_enums(): void
154+
{
155+
$object = map(['roles' => ['admin', 'user']])->to(ObjectWithArrayEnumProperty::class);
156+
157+
$this->assertCount(2, $object->roles);
158+
$this->assertSame(EnumToBeMappedToArray::ADMIN, $object->roles[0]);
159+
$this->assertSame(EnumToBeMappedToArray::USER, $object->roles[1]);
160+
}
161+
162+
public function test_map_array_of_serialized_enums(): void
163+
{
164+
$object = map(['roles' => json_encode(['admin'])])->to(ObjectWithArrayEnumProperty::class);
165+
166+
$this->assertCount(1, $object->roles);
167+
$this->assertSame(EnumToBeMappedToArray::ADMIN, $object->roles[0]);
168+
}
169+
}
170+
171+
final class ObjectWithArrayEnumProperty
172+
{
173+
/** @var \Tests\Tempest\Integration\Mapper\Mappers\EnumToBeMappedToArray[] */
174+
public array $roles;
175+
}
176+
177+
enum EnumToBeMappedToArray: string
178+
{
179+
case ADMIN = 'admin';
180+
case USER = 'user';
152181
}

0 commit comments

Comments
 (0)