Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/LiveComponent/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## 2.32

- Add support for `LiveProp` union types in Live Components.
`LiveComponentMetadataFactory` can now create `liveProp` metadata for union types.
Note: This feature only works when using a custom `hydrate` and `dehydrate` implementation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @kevinmade ! Could you add a test or two covering the cases "not supposed to work" (ie: when hydrate and/or dehydrate are missing ?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought a hydrator and dehydrator were required for my change, but this had something to do with my own use case, where the union had a type that wasn't a hydratable. I’ve removed the note from the changelog and added a test.


## 2.31

- Add browser events assertions in `InteractsWithLiveComponents`:
Expand Down
11 changes: 8 additions & 3 deletions src/LiveComponent/src/Metadata/LiveComponentMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,13 @@ public function createPropMetadatas(\ReflectionClass $class): array
public function createLivePropMetadata(string $className, string $propertyName, \ReflectionProperty $property, LiveProp $liveProp): LivePropMetadata|LegacyLivePropMetadata
{
$reflectionType = $property->getType();
if ($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) {
throw new \LogicException(\sprintf('Union or intersection types are not supported for LiveProps. You may want to change the type of property "%s" in "%s".', $property->getName(), $property->getDeclaringClass()->getName()));
}

// BC layer when "symfony/type-info" is not available
if (!method_exists($this->propertyTypeExtractor, 'getType')) {
if ($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) {
return new LegacyLivePropMetadata($property->getName(), $liveProp, 'mixed', true, $reflectionType->allowsNull(), null);
}

$infoTypes = $this->propertyTypeExtractor->getTypes($className, $propertyName) ?? [];

$collectionValueType = null;
Expand Down Expand Up @@ -122,6 +123,10 @@ public function createLivePropMetadata(string $className, string $propertyName,
$collectionValueType
);
} else {
if ($reflectionType instanceof \ReflectionUnionType || $reflectionType instanceof \ReflectionIntersectionType) {
return new LivePropMetadata($property->getName(), $liveProp, Type::mixed());
}

$infoType = $this->propertyTypeExtractor->getType($className, $property->getName());

if ($infoType instanceof CollectionType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\LiveComponent\Tests\Fixtures\Component;

use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\IntEnum;
use Symfony\UX\LiveComponent\Tests\Fixtures\Enum\ZeroIntEnum;

#[AsLiveComponent('with_union_intersection_type')]
final class WithUnionIntersectionType
{
use DefaultActionTrait;

#[LiveProp]
public int|float|null $unionProp = null;

#[LiveProp]
public IntEnum&ZeroIntEnum $intersectionProp;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\UX\LiveComponent\Metadata\LiveComponentMetadataFactory;
use Symfony\UX\LiveComponent\Metadata\UrlMapping;
use Symfony\UX\LiveComponent\Tests\Fixtures\Component\ComponentWithUrlBoundProps;
use Symfony\UX\LiveComponent\Tests\Fixtures\Component\WithUnionIntersectionType;

class LiveComponentMetadataFactoryTest extends KernelTestCase
{
Expand Down Expand Up @@ -42,4 +43,21 @@ public function testQueryStringMapping()
$this->assertEquals(new UrlMapping(as: 'q'), $propsMetadataByName['boundPropWithAlias']->urlMapping());
$this->assertNotNull($propsMetadataByName['boundPropWithCustomAlias']);
}

public function testLivePropUnionType()
{
/** @var LiveComponentMetadataFactory $metadataFactory */
$metadataFactory = self::getContainer()->get('ux.live_component.metadata_factory');

$class = new \ReflectionClass(WithUnionIntersectionType::class);
$propsMetadata = $metadataFactory->createPropMetadatas($class);

$propsMetadataByName = [];
foreach ($propsMetadata as $propMetadata) {
$propsMetadataByName[$propMetadata->getName()] = $propMetadata;
}

$this->assertEquals('mixed', (string) $propsMetadataByName['unionProp']->getType());
$this->assertEquals('mixed', (string) $propsMetadataByName['intersectionProp']->getType());
}
}
Loading