diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index c668df6fd62..cc523adf6bd 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -119,9 +119,9 @@ public function normalize(mixed $object, ?string $format = null, array $context $metadata = $this->createJsonLdContext($this->contextBuilder, $object, $context); } - // maybe not needed anymore - if (isset($context['operation']) && $previousResourceClass !== $resourceClass) { - unset($context['operation'], $context['operation_name']); + // Special case: non-resource got serialized and contains a resource therefore we need to reset part of the context + if ($previousResourceClass !== $resourceClass) { + unset($context['operation'], $context['operation_name'], $context['output']); } if (true === ($context['output']['gen_id'] ?? true) && true === ($context['force_iri_generation'] ?? true) && $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_PATH, $context['operation'] ?? null, $context)) { @@ -136,9 +136,12 @@ public function normalize(mixed $object, ?string $format = null, array $context return $data; } - if (!isset($metadata['@type']) && $isResourceClass) { - $operation = $context['operation'] ?? $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(); + $operation = $context['operation'] ?? null; + if ($isResourceClass && !$operation) { + $operation = $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation(); + } + if (!isset($metadata['@type']) && $operation) { $types = $operation instanceof HttpOperation ? $operation->getTypes() : null; if (null === $types) { $types = [$operation->getShortName()]; diff --git a/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelFirst.php b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelFirst.php new file mode 100644 index 00000000000..804eab26212 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelFirst.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[ApiResource(operations: [new Get(uriTemplate: '/levelfirst/{id}', provider: [self::class, 'provider'])])] +class LevelFirst +{ + /** + * @param list $levelSecond + */ + public function __construct(public string $id, #[ApiProperty(genId: false)] public array $levelSecond) + { + } + + public static function provider(Operation $operation, array $uriVariables = [], array $context = []): self + { + return new self($uriVariables['id'], [new LevelSecond(new LevelThird('3', 'L3 Name'))]); + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelSecond.php b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelSecond.php new file mode 100644 index 00000000000..7145fe7dcba --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelSecond.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse; + +class LevelSecond +{ + public function __construct(public LevelThird $levelThird) + { + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelThird.php b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelThird.php new file mode 100644 index 00000000000..d8c5a35e8b3 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/GenIdFalse/LevelThird.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse; + +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; + +#[ApiResource(operations: [new Get(uriTemplate: '/levelthird/{id}')])] +class LevelThird +{ + public function __construct(public string $id, public string $name) + { + } +} diff --git a/tests/Functional/JsonLdTest.php b/tests/Functional/JsonLdTest.php index 2b2e8f698bb..62188b6bffb 100644 --- a/tests/Functional/JsonLdTest.php +++ b/tests/Functional/JsonLdTest.php @@ -16,6 +16,8 @@ use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\AggregateRating; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\GenIdFalse; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\LevelFirst; +use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\GenIdFalse\LevelThird; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6810\JsonLdContextOutput; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Bar; use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Foo; @@ -34,7 +36,7 @@ class JsonLdTest extends ApiTestCase */ public static function getResources(): array { - return [Foo::class, Bar::class, JsonLdContextOutput::class, GenIdFalse::class, AggregateRating::class]; + return [Foo::class, Bar::class, JsonLdContextOutput::class, GenIdFalse::class, AggregateRating::class, LevelFirst::class, LevelThird::class]; } /** @@ -81,6 +83,17 @@ public function testGenIdFalseOnResource(): void $this->assertArrayNotHasKey('@id', $r->toArray()['aggregateRating']); } + public function testGenIdFalseOnNestedResource(): void + { + $r = self::createClient()->request( + 'GET', + '/levelfirst/1', + ); + $res = $r->toArray(); + $this->assertArrayNotHasKey('@id', $res['levelSecond']); + $this->assertArrayHasKey('@id', $res['levelSecond'][0]['levelThird']); + } + public function testShouldIgnoreProperty(): void { $r = self::createClient()->request(