From 2edf04cb24faf34cf092c705799f94f6b2ba9e59 Mon Sep 17 00:00:00 2001 From: soyuka Date: Tue, 7 May 2024 13:46:27 +0200 Subject: [PATCH 1/2] fix(symfony): no read should not throw on wrong uri variables fixes #6358 --- src/Serializer/SerializerContextBuilder.php | 2 +- src/State/Processor/SerializeProcessor.php | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Serializer/SerializerContextBuilder.php b/src/Serializer/SerializerContextBuilder.php index ee17ba5609..b7730c1f88 100644 --- a/src/Serializer/SerializerContextBuilder.php +++ b/src/Serializer/SerializerContextBuilder.php @@ -81,7 +81,7 @@ public function createFromRequest(Request $request, bool $normalization, ?array } } - if (($options = $operation?->getStateOptions()) && $options instanceof Options && $options->getEntityClass()) { + if (null === $context['output'] && ($options = $operation?->getStateOptions()) && $options instanceof Options && $options->getEntityClass()) { $context['force_resource_class'] = $operation->getClass(); } diff --git a/src/State/Processor/SerializeProcessor.php b/src/State/Processor/SerializeProcessor.php index a2cc9da24d..eccc2d8b55 100644 --- a/src/State/Processor/SerializeProcessor.php +++ b/src/State/Processor/SerializeProcessor.php @@ -51,8 +51,14 @@ public function process(mixed $data, Operation $operation, array $uriVariables = // @see ApiPlatform\State\Processor\RespondProcessor $context['original_data'] = $data; + $class = $operation->getClass(); + if ($request->attributes->get('_api_resource_class') !== $operation->getClass()) { + $class = $request->attributes->get('_api_resource_class'); + trigger_deprecation('api-platform/core', '3.3', 'The resource class on the router is not the same as the operation\'s class which leads to wrong behaviors. Prefer using "stateOptions" if you need to change the entity class.'); + } + $serializerContext = $this->serializerContextBuilder->createFromRequest($request, true, [ - 'resource_class' => $operation->getClass(), + 'resource_class' => $class, 'operation' => $operation, ]); From 4e76fbc79a9a018565477fa36dbe2d10e2ff8bc4 Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 10 May 2024 11:20:10 +0200 Subject: [PATCH 2/2] add tests --- features/main/input_output.feature | 15 ++++++++ src/State/Processor/SerializeProcessor.php | 2 +- .../Issue6358/OutputAndEntityClass.php | 35 +++++++++++++++++++ .../Issue6358/OutputAndEntityClassDto.php | 18 ++++++++++ .../Issue6358/OutputAndEntityClassEntity.php | 28 +++++++++++++++ .../TestBundle/Entity/IdentifierShortcut.php | 22 ++++++++++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 features/main/input_output.feature create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClass.php create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassDto.php create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassEntity.php create mode 100644 tests/Fixtures/TestBundle/Entity/IdentifierShortcut.php diff --git a/features/main/input_output.feature b/features/main/input_output.feature new file mode 100644 index 0000000000..0c6f49926d --- /dev/null +++ b/features/main/input_output.feature @@ -0,0 +1,15 @@ +Feature: DTO input and output + In order to use a hypermedia API + As a client software developer + I need to be able to use DTOs on my resources as Input or Output objects. + + Background: + Given I add "Accept" header equal to "application/ld+json" + And I add "Content-Type" header equal to "application/ld+json" + + @!mongodb + Scenario: Fetch a collection of outputs with an entityClass as state option + When I send a "GET" request to "/output_and_entity_classes" + And the JSON node "hydra:member[0].@type" should be equal to "OutputAndEntityClassEntity" + + diff --git a/src/State/Processor/SerializeProcessor.php b/src/State/Processor/SerializeProcessor.php index eccc2d8b55..5f399f889f 100644 --- a/src/State/Processor/SerializeProcessor.php +++ b/src/State/Processor/SerializeProcessor.php @@ -52,7 +52,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables = $context['original_data'] = $data; $class = $operation->getClass(); - if ($request->attributes->get('_api_resource_class') !== $operation->getClass()) { + if ($request->attributes->get('_api_resource_class') && $request->attributes->get('_api_resource_class') !== $operation->getClass()) { $class = $request->attributes->get('_api_resource_class'); trigger_deprecation('api-platform/core', '3.3', 'The resource class on the router is not the same as the operation\'s class which leads to wrong behaviors. Prefer using "stateOptions" if you need to change the entity class.'); } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClass.php b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClass.php new file mode 100644 index 0000000000..1ca1f9b6db --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClass.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\Issue6358; + +use ApiPlatform\Doctrine\Orm\State\Options; +use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\GetCollection; + +#[ApiResource( + operations: [ + new GetCollection( + output: OutputAndEntityClassDto::class, + provider: [self::class, 'provide'], + stateOptions: new Options(entityClass: OutputAndEntityClassEntity::class) + ), + ], +)] +class OutputAndEntityClass +{ + public static function provide(): array + { + return [new OutputAndEntityClassEntity(1)]; + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassDto.php b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassDto.php new file mode 100644 index 0000000000..142bf1f24b --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassDto.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\ApiResource\Issue6358; + +class OutputAndEntityClassDto +{ +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassEntity.php b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassEntity.php new file mode 100644 index 0000000000..d4cf9120bc --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue6358/OutputAndEntityClassEntity.php @@ -0,0 +1,28 @@ + + * + * 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\Issue6358; + +use Doctrine\ORM\Mapping as ORM; + +#[ORM\Entity] +class OutputAndEntityClassEntity +{ + public function __construct( + #[ORM\Id] + #[ORM\Column(type: 'integer')] + #[ORM\GeneratedValue(strategy: 'AUTO')] + public readonly ?int $id = null + ) { + } +} diff --git a/tests/Fixtures/TestBundle/Entity/IdentifierShortcut.php b/tests/Fixtures/TestBundle/Entity/IdentifierShortcut.php new file mode 100644 index 0000000000..ffa3eadfd7 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/IdentifierShortcut.php @@ -0,0 +1,22 @@ + + * + * 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; + +use ApiPlatform\Metadata\Patch; + +#[Patch(uriTemplate: '/identifiers_shortcut/{id}', uriVariables: [self::class, 'id'])] +class IdentifierShortcut +{ + public $id; +}