Skip to content

Commit 23a8db6

Browse files
committed
feat(database): support union data transfer object casting
1 parent 9fcfa18 commit 23a8db6

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

packages/database/src/Casters/DataTransferObjectCaster.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ public static function accepts(PropertyReflector|TypeReflector $type): bool
3333
? $type->getType()
3434
: $type;
3535

36+
if ($type->isUnion()) {
37+
foreach ($type->split() as $memberType) {
38+
if (static::accepts($memberType)) {
39+
return true;
40+
}
41+
}
42+
43+
return false;
44+
}
45+
3646
return $type->isClass() && $type->asClass()->getAttribute(SerializeAs::class) !== null;
3747
}
3848

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
namespace Tests\Tempest\Integration\Database\DtoSerialization;
4+
5+
use PHPUnit\Framework\Attributes\Test;
6+
use Tempest\Database\MigratesUp;
7+
use Tempest\Database\Migrations\CreateMigrationsTable;
8+
use Tempest\Database\QueryStatement;
9+
use Tempest\Database\QueryStatements\CreateTableStatement;
10+
use Tempest\Mapper\MapperConfig;
11+
use Tempest\Mapper\SerializeAs;
12+
use Tests\Tempest\Integration\FrameworkIntegrationTestCase;
13+
14+
use function Tempest\Database\query;
15+
16+
final class UnionDtoSerializationTest extends FrameworkIntegrationTestCase
17+
{
18+
#[Test]
19+
public function can_serialize_union_object(): void
20+
{
21+
$config = $this->container->get(MapperConfig::class);
22+
$config->serializeAs(TestUnionA::class, 'test_union_a');
23+
$config->serializeAs(TestUnionB::class, 'test_union_b');
24+
$config->serializeAs(TestNestedObject::class, 'test_object');
25+
26+
$this->database->migrate(CreateMigrationsTable::class, new class implements MigratesUp {
27+
public string $name = '001_union_serialization';
28+
29+
public function up(): QueryStatement
30+
{
31+
return new CreateTableStatement('test_wrapper_models')
32+
->primary()
33+
->json('property');
34+
}
35+
});
36+
37+
query(TestWrapperModel::class)->create(property: new TestUnionA(
38+
content: 'abc',
39+
testObject: new TestNestedObject('def'),
40+
));
41+
42+
query(TestWrapperModel::class)->create(property: new TestUnionB(
43+
message: '123',
44+
testObject: new TestNestedObject('456'),
45+
));
46+
47+
$results = query(TestWrapperModel::class)->all();
48+
49+
$this->assertCount(2, $results);
50+
$this->assertInstanceOf(TestUnionA::class, $results[0]->property);
51+
$this->assertSame('abc', $results[0]->property->content);
52+
$this->assertInstanceOf(TestNestedObject::class, $results[0]->property->testObject);
53+
$this->assertSame('def', $results[0]->property->testObject->content);
54+
55+
$this->assertInstanceOf(TestUnionB::class, $results[1]->property);
56+
$this->assertSame('123', $results[1]->property->message);
57+
$this->assertInstanceOf(TestNestedObject::class, $results[1]->property->testObject);
58+
$this->assertSame('456', $results[1]->property->testObject->content);
59+
}
60+
}
61+
62+
final class TestWrapperModel
63+
{
64+
public function __construct(
65+
public TestUnionA|TestUnionB $property,
66+
) {}
67+
}
68+
69+
#[SerializeAs('test_object')]
70+
final class TestNestedObject
71+
{
72+
public function __construct(
73+
public string $content,
74+
) {}
75+
}
76+
77+
#[SerializeAs('test_union_a')]
78+
final class TestUnionA
79+
{
80+
public function __construct(
81+
public string $content,
82+
public TestNestedObject $testObject,
83+
) {}
84+
}
85+
86+
#[SerializeAs('test_union_b')]
87+
final class TestUnionB
88+
{
89+
public function __construct(
90+
public string $message,
91+
public TestNestedObject $testObject,
92+
) {}
93+
}

0 commit comments

Comments
 (0)