Fix serializing collection with intersection types

This commit is contained in:
Julien Fastré 2023-08-30 20:25:35 +02:00
parent 711cdc3481
commit bad3cdca09
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
2 changed files with 111 additions and 4 deletions

View File

@ -96,7 +96,10 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
return 'docgen' === $format && (is_object($data) || null === $data);
}
private function getExpectedType(AttributeMetadata $attribute, ReflectionClass $reflection): string
/**
* @return string|array<string>
*/
private function getExpectedType(AttributeMetadata $attribute, ReflectionClass $reflection): string|array
{
$type = null;
@ -147,14 +150,31 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
}
} while (null === $type && $reflection instanceof ReflectionClass);
if (null === $type ?? null) {
if ($type instanceof \ReflectionNamedType) {
return $type->getName();
}
if ($type instanceof \ReflectionIntersectionType) {
foreach (array_map(fn (\ReflectionNamedType $t) => $t->getName(), $type->getTypes()) as $classString) {
if (ReadableCollection::class === $classString) {
return ReadableCollection::class;
}
$class = new ReflectionClass($classString);
if ($class->implementsInterface(ReadableCollection::class)) {
return ReadableCollection::class;
}
}
throw new \LogicException(sprintf("The automatic normalization of intersection types is not supported, unless a %s is contained in the intersected types", ReadableCollection::class));
} elseif (null === $type ?? null) {
throw new \LogicException(sprintf(
'Could not determine the type for this attribute: %s. Add a return type to the method or property declaration',
$attribute->getName()
));
}
return $type->getName();
throw new \LogicException(sprintf("The automatic normalization of %s is not supported", $type::class));
}
/**

View File

@ -13,6 +13,10 @@ namespace Chill\DocGeneratorBundle\tests\Serializer\Normalizer;
use Chill\MainBundle\Entity\Scope;
use Chill\MainBundle\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
@ -28,7 +32,6 @@ final class DocGenObjectNormalizerTest extends KernelTestCase
protected function setUp(): void
{
parent::setUp();
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
@ -171,6 +174,64 @@ final class DocGenObjectNormalizerTest extends KernelTestCase
$this->assertEquals($expected, $normalized, 'test normalization fo an user with null center');
}
public function testIntersectionTypeForReadableCollection(): void
{
$value = new TestableWithIntersectionReadableCollection();
$normalized = $this->normalizer->normalize($value, 'docgen', [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => TestableWithIntersectionReadableCollection::class]);
self::assertIsArray($normalized);
self::assertIsArray($normalized['collection']);
self::assertCount(2, $normalized['collection']);
self::assertEquals(
['baz' => 'bloup', 'isNull' => false],
$normalized['collection'][0]
);
self::assertEquals(
['baz' => 'bloup', 'isNull' => false],
$normalized['collection'][1]
);
}
public function testIntersectionTypeForReadableCollectionWithNullValue(): void
{
$normalized = $this->normalizer->normalize(null, 'docgen', [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => TestableWithIntersectionReadableCollection::class]);
self::assertIsArray($normalized);
self::assertIsArray($normalized['collection']);
self::assertCount(0, $normalized['collection']);
}
public function testIntersectionTypeForCollection(): void
{
$value = new TestableWithCollection();
$normalized = $this->normalizer->normalize($value, 'docgen', [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => TestableWithCollection::class]);
self::assertIsArray($normalized);
self::assertIsArray($normalized['collection']);
self::assertCount(2, $normalized['collection']);
self::assertEquals(
['baz' => 'bloup', 'isNull' => false],
$normalized['collection'][0]
);
self::assertEquals(
['baz' => 'bloup', 'isNull' => false],
$normalized['collection'][1]
);
}
public function testIntersectionTypeForCollectionWithNullValue(): void
{
$normalized = $this->normalizer->normalize(null, 'docgen', [AbstractNormalizer::GROUPS => ['docgen:read'], 'docgen:expects' => TestableWithCollection::class]);
self::assertIsArray($normalized);
self::assertIsArray($normalized['collection']);
self::assertCount(0, $normalized['collection']);
}
}
class TestableParentClass
@ -215,3 +276,29 @@ class TestableClassWithBool
return true;
}
}
class TestableWithIntersectionReadableCollection
{
/**
* @Serializer\Groups("docgen:read")
*/
public ReadableCollection&Selectable $collection;
public function __construct()
{
$this->collection = new ArrayCollection([new TestableChildClass(), new TestableChildClass()]);
}
}
class TestableWithCollection
{
/**
* @Serializer\Groups("docgen:read")
*/
public Collection&Selectable $collection;
public function __construct()
{
$this->collection = new ArrayCollection([new TestableChildClass(), new TestableChildClass()]);
}
}