diff --git a/features/issues/5926.feature b/features/issues/5926.feature new file mode 100644 index 00000000000..668d205a095 --- /dev/null +++ b/features/issues/5926.feature @@ -0,0 +1,12 @@ +Feature: Issue 5926 + In order to reproduce the issue at https://github.com/api-platform/core/issues/5926 + As a client software developer + I need to be able to use every operation on a resource with non-resources embed objects + + @!mongodb + Scenario: Create and retrieve a WriteResource + When I add "Accept" header equal to "application/json" + And I send a "GET" request to "/test_issue5926s/1" + Then the response status code should be 200 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/json; charset=utf-8" diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index 819c20fc603..5eafbc7a31e 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -81,8 +81,8 @@ public function normalize(mixed $object, string $format = null, array $context = // TODO: we should not remove the resource_class in the normalizeRawCollection as we would find out anyway that it's not the same as the requested one $previousResourceClass = $context['resource_class'] ?? null; $metadata = []; - if ($isResourceClass = $this->resourceClassResolver->isResourceClass($resourceClass)) { - $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null); + if ($isResourceClass = $this->resourceClassResolver->isResourceClass($resourceClass) && (null === $previousResourceClass || $this->resourceClassResolver->isResourceClass($previousResourceClass))) { + $resourceClass = $this->resourceClassResolver->getResourceClass($object, $previousResourceClass); $context = $this->initContext($resourceClass, $context); $metadata = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context); } elseif ($this->contextBuilder instanceof AnonymousContextBuilderInterface) { diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index b3f694a3b04..d0ab05c528c 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -710,8 +710,8 @@ protected function getAttributeValue(object $object, string $attribute, string $ ); // Anonymous resources - if ($type->getClassName()) { - $childContext = $this->createChildContext($this->createOperationContext($context, null), $attribute, $format); + if ($className) { + $childContext = $this->createChildContext($this->createOperationContext($context, $className), $attribute, $format); $childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? true; $attributeValue = $this->propertyAccessor->getValue($object, $attribute); diff --git a/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemCollection.php b/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemCollection.php new file mode 100644 index 00000000000..d9f3c6ba540 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemCollection.php @@ -0,0 +1,29 @@ + + * + * 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\Entity\Issue5926; + +class ContentItemCollection implements \IteratorAggregate +{ + private array $items; + + public function __construct(ContentItemInterface ...$items) + { + $this->items = $items; + } + + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->items); + } +} diff --git a/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemInterface.php b/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemInterface.php new file mode 100644 index 00000000000..83638913642 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue5926/ContentItemInterface.php @@ -0,0 +1,18 @@ + + * + * 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\Entity\Issue5926; + +interface ContentItemInterface +{ +} diff --git a/tests/Fixtures/TestBundle/Entity/Issue5926/Media.php b/tests/Fixtures/TestBundle/Entity/Issue5926/Media.php new file mode 100644 index 00000000000..edcf962e0df --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue5926/Media.php @@ -0,0 +1,36 @@ + + * + * 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\Entity\Issue5926; + +use ApiPlatform\Metadata\ApiResource; + +#[ApiResource] +class Media +{ + public function __construct( + private readonly string $id, + private readonly string $title, + ) { + } + + public function getId(): string + { + return $this->id; + } + + public function getTitle(): string + { + return $this->title; + } +} diff --git a/tests/Fixtures/TestBundle/Entity/Issue5926/MediaContentItem.php b/tests/Fixtures/TestBundle/Entity/Issue5926/MediaContentItem.php new file mode 100644 index 00000000000..1cbc6c5e22a --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue5926/MediaContentItem.php @@ -0,0 +1,27 @@ + + * + * 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\Entity\Issue5926; + +class MediaContentItem implements ContentItemInterface +{ + public function __construct( + private readonly Media $media, + ) { + } + + public function getMedia(): Media + { + return $this->media; + } +} diff --git a/tests/Fixtures/TestBundle/Entity/Issue5926/TestIssue5926.php b/tests/Fixtures/TestBundle/Entity/Issue5926/TestIssue5926.php new file mode 100644 index 00000000000..e62661bfbd5 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/Issue5926/TestIssue5926.php @@ -0,0 +1,56 @@ + + * + * 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\Entity\Issue5926; + +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[ApiResource( + operations: [ + new Get( + provider: [TestIssue5926::class, 'provide'] + ), + ] +)] +class TestIssue5926 +{ + public function __construct( + private readonly string $id, + private readonly ?ContentItemCollection $content, + ) { + } + + public function getId(): string + { + return $this->id; + } + + public function getContent(): ?ContentItemCollection + { + return $this->content; + } + + public static function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null + { + $media = new Media('1', 'My media'); + $contentItem1 = new MediaContentItem($media); + $media = new Media('2', 'My media 2'); + $contentItem2 = new MediaContentItem($media); + + $collection = new ContentItemCollection($contentItem1, $contentItem2); + + return new self('1', $collection); + } +}