diff --git a/src/Core/Hydra/Serializer/CollectionNormalizer.php b/src/Core/Hydra/Serializer/CollectionNormalizer.php index f6889d2ccfe..468c29f82a9 100644 --- a/src/Core/Hydra/Serializer/CollectionNormalizer.php +++ b/src/Core/Hydra/Serializer/CollectionNormalizer.php @@ -23,6 +23,7 @@ use ApiPlatform\Core\JsonLd\ContextBuilderInterface; use ApiPlatform\Core\JsonLd\Serializer\JsonLdContextTrait; use ApiPlatform\Core\Serializer\ContextTrait; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; @@ -78,6 +79,9 @@ public function supportsNormalization($data, $format = null) public function normalize($object, $format = null, array $context = []) { if (!isset($context['resource_class']) || isset($context['api_sub_level'])) { + if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) && $object instanceof \Countable && 0 === $object->count()) { + return $object; + } return $this->normalizeRawCollection($object, $format, $context); } diff --git a/src/Core/Serializer/AbstractCollectionNormalizer.php b/src/Core/Serializer/AbstractCollectionNormalizer.php index 1c298d43388..4591e43a471 100644 --- a/src/Core/Serializer/AbstractCollectionNormalizer.php +++ b/src/Core/Serializer/AbstractCollectionNormalizer.php @@ -18,6 +18,7 @@ use ApiPlatform\Core\DataProvider\PartialPaginatorInterface; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; @@ -79,6 +80,9 @@ public function hasCacheableSupportsMethod(): bool public function normalize($object, $format = null, array $context = []) { if (!isset($context['resource_class']) || isset($context['api_sub_level'])) { + if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) && $object instanceof \Countable && 0 === $object->count()) { + return $object; + } return $this->normalizeRawCollection($object, $format, $context); } diff --git a/tests/Core/Hydra/Serializer/CollectionNormalizerTest.php b/tests/Core/Hydra/Serializer/CollectionNormalizerTest.php index b1b789531c7..94da8942d71 100644 --- a/tests/Core/Hydra/Serializer/CollectionNormalizerTest.php +++ b/tests/Core/Hydra/Serializer/CollectionNormalizerTest.php @@ -27,6 +27,7 @@ use ApiPlatform\Tests\Fixtures\NotAResource; use PHPUnit\Framework\TestCase; use Prophecy\Argument; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -256,6 +257,27 @@ public function testNormalizeSubLevelNonResourceCollection() ], $actual); } + public function testPreserveEmptyObject() + { + $data = new \ArrayObject(); + + $contextBuilderProphecy = $this->prophesize(ContextBuilderInterface::class); + $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); + $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); + + $normalizer = new CollectionNormalizer($contextBuilderProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $iriConverterProphecy->reveal()); + + $actual = $normalizer->normalize($data, CollectionNormalizer::FORMAT, [ + AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true + ]); + + $this->assertSame($data, $actual); + + $actual = $normalizer->normalize($data, CollectionNormalizer::FORMAT, []); + + $this->assertSame([], $actual); + } + public function testNormalizePaginator() { $this->assertEquals( diff --git a/tests/Core/JsonApi/Serializer/CollectionNormalizerTest.php b/tests/Core/JsonApi/Serializer/CollectionNormalizerTest.php index c9c48bb6750..91861596a93 100644 --- a/tests/Core/JsonApi/Serializer/CollectionNormalizerTest.php +++ b/tests/Core/JsonApi/Serializer/CollectionNormalizerTest.php @@ -22,6 +22,7 @@ use ApiPlatform\Core\Tests\ProphecyTrait; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; /** @@ -338,4 +339,24 @@ public function testNormalizeWithoutDataKey() 'resource_class' => 'Foo', ]); } + + public function testPreserveEmptyObject() + { + $data = new \ArrayObject(); + + $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); + $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + + $normalizer = new CollectionNormalizer($resourceClassResolverProphecy->reveal(), 'page', $resourceMetadataFactoryProphecy->reveal()); + + $actual = $normalizer->normalize($data, CollectionNormalizer::FORMAT, [ + AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true + ]); + + $this->assertSame($data, $actual); + + $actual = $normalizer->normalize($data, CollectionNormalizer::FORMAT, []); + + $this->assertSame([], $actual); + } }