Skip to content

Commit 5893aca

Browse files
committed
bug #416 [Live] Support (de)hydrating nullable backed Enum's (kbond)
This PR was merged into the 2.x branch. Discussion ---------- [Live] Support (de)hydrating nullable backed Enum's | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | Tickets | Fix #414 | License | MIT Commits ------- bfd7f8d [Live] support (de)hydrating Enum's
2 parents 9aa183d + bfd7f8d commit 5893aca

File tree

7 files changed

+128
-2
lines changed

7 files changed

+128
-2
lines changed

src/LiveComponent/src/LiveComponentHydrator.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,15 @@ public function hydrate(object $component, array $data, string $componentName):
166166
}
167167

168168
$value = $dehydratedValue;
169+
$type = $property->getType() instanceof \ReflectionNamedType ? $property->getType() : null;
169170

170171
if ($method = $liveProp->hydrateMethod()) {
171172
// TODO: Error checking
172173
$value = $component->$method($dehydratedValue);
173-
} elseif ($property->getType() instanceof \ReflectionNamedType && !$property->getType()->isBuiltin()) {
174-
$value = $this->normalizer->denormalize($value, $property->getType()->getName(), 'json', [self::LIVE_CONTEXT => true]);
174+
} elseif (!$value && $type && $type->allowsNull() && is_a($type->getName(), \BackedEnum::class, true) && !\in_array($value, array_map(fn (\BackedEnum $e) => $e->value, $type->getName()::cases()))) {
175+
$value = null;
176+
} elseif (null !== $value && $type && !$type->isBuiltin()) {
177+
$value = $this->normalizer->denormalize($value, $type->getName(), 'json', [self::LIVE_CONTEXT => true]);
175178
}
176179

177180
foreach ($liveProp->exposed() as $exposedProperty) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;
4+
5+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
6+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
7+
use Symfony\UX\LiveComponent\DefaultActionTrait;
8+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\EmptyStringEnum;
9+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\IntEnum;
10+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\StringEnum;
11+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\ZeroIntEnum;
12+
13+
#[AsLiveComponent('with_enum')]
14+
final class WithEnums
15+
{
16+
use DefaultActionTrait;
17+
18+
#[LiveProp(writable: true)]
19+
public ?IntEnum $int = null;
20+
21+
#[LiveProp(writable: true)]
22+
public ?StringEnum $string = null;
23+
24+
#[LiveProp(writable: true)]
25+
public ?ZeroIntEnum $zeroInt = null;
26+
27+
#[LiveProp(writable: true)]
28+
public ?EmptyStringEnum $emptyString = null;
29+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Enum;
4+
5+
enum EmptyStringEnum: string
6+
{
7+
case EMPTY = '';
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Enum;
4+
5+
enum IntEnum: int
6+
{
7+
case HIGH = 10;
8+
case LOW = 1;
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Enum;
4+
5+
enum StringEnum: string
6+
{
7+
case PENDING = 'pending';
8+
case ACTIVE = 'active';
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\UX\LiveComponent\Tests\Fixtures\Enum;
4+
5+
enum ZeroIntEnum: int
6+
{
7+
case ZERO = 0;
8+
}

src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
use Symfony\UX\LiveComponent\Tests\Fixtures\Component\Component3;
1818
use Symfony\UX\LiveComponent\Tests\Fixtures\Component\ComponentWithArrayProp;
1919
use Symfony\UX\LiveComponent\Tests\Fixtures\Entity\Entity1;
20+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\EmptyStringEnum;
21+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\IntEnum;
22+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\StringEnum;
23+
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\ZeroIntEnum;
2024
use Symfony\UX\LiveComponent\Tests\LiveComponentTestHelper;
2125
use Zenstruck\Foundry\Test\Factories;
2226
use Zenstruck\Foundry\Test\ResetDatabase;
@@ -279,4 +283,60 @@ public function testCanDehydrateAndHydrateComponentsWithEmptyAttributes(): void
279283

280284
$this->assertSame([], $mounted->getAttributes()->all());
281285
}
286+
287+
/**
288+
* @requires PHP >= 8.1
289+
*/
290+
public function testCanHydrateEnums(): void
291+
{
292+
$mounted = $this->mountComponent('with_enum');
293+
294+
$dehydrated = $this->dehydrateComponent($mounted);
295+
296+
$this->assertNull($dehydrated['int']);
297+
$this->assertNull($dehydrated['string']);
298+
299+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
300+
301+
$this->assertNull($mounted->getComponent()->int);
302+
$this->assertNull($mounted->getComponent()->string);
303+
304+
$dehydrated['int'] = IntEnum::LOW->value;
305+
$dehydrated['string'] = StringEnum::PENDING->value;
306+
307+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
308+
309+
$this->assertSame(IntEnum::LOW, $mounted->getComponent()->int);
310+
$this->assertSame(StringEnum::PENDING, $mounted->getComponent()->string);
311+
312+
$dehydrated['int'] = null;
313+
$dehydrated['string'] = null;
314+
315+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
316+
317+
$this->assertNull($mounted->getComponent()->int);
318+
$this->assertNull($mounted->getComponent()->string);
319+
320+
$dehydrated['int'] = '';
321+
$dehydrated['string'] = '';
322+
323+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
324+
325+
$this->assertNull($mounted->getComponent()->int);
326+
$this->assertNull($mounted->getComponent()->string);
327+
328+
$dehydrated['zeroInt'] = 0;
329+
$dehydrated['emptyString'] = '';
330+
331+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
332+
333+
$this->assertSame(ZeroIntEnum::ZERO, $mounted->getComponent()->zeroInt);
334+
$this->assertSame(EmptyStringEnum::EMPTY, $mounted->getComponent()->emptyString);
335+
336+
$dehydrated['zeroInt'] = '0';
337+
338+
$mounted = $this->hydrateComponent($this->getComponent('with_enum'), $dehydrated, $mounted->getName());
339+
340+
$this->assertSame(ZeroIntEnum::ZERO, $mounted->getComponent()->zeroInt);
341+
}
282342
}

0 commit comments

Comments
 (0)