diff --git a/features/bootstrap/DoctrineContext.php b/features/bootstrap/DoctrineContext.php index 3901c90bc66..c6c4ba87e9e 100644 --- a/features/bootstrap/DoctrineContext.php +++ b/features/bootstrap/DoctrineContext.php @@ -53,6 +53,7 @@ use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\FooDummy as FooDummyDocument; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\FourthLevel as FourthLevelDocument; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\Greeting as GreetingDocument; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\InitializeInput as InitializeInputDocument; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\MaxDepthDummy as MaxDepthDummyDocument; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\NetworkPathDummy as NetworkPathDummyDocument; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\NetworkPathRelationDummy as NetworkPathRelationDummyDocument; @@ -117,6 +118,7 @@ use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FooDummy; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\FourthLevel; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Greeting; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InitializeInput; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InternalUser; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\MaxDepthDummy; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\NetworkPathDummy; @@ -1589,6 +1591,20 @@ public function thereAreNetworkPathDummies(int $nb) $this->manager->flush(); } + /** + * @Given there is an InitializeInput object with id :id + */ + public function thereIsAnInitializeInput(int $id) + { + $initializeInput = $this->buildInitializeInput(); + $initializeInput->id = $id; + $initializeInput->manager = 'Orwell'; + $initializeInput->name = '1984'; + + $this->manager->persist($initializeInput); + $this->manager->flush(); + } + private function isOrm(): bool { return null !== $this->schemaTool; @@ -2006,4 +2022,12 @@ private function buildNetworkPathRelationDummy() { return $this->isOrm() ? new NetworkPathRelationDummy() : new NetworkPathRelationDummyDocument(); } + + /** + * @return InitializeInput|InitializeInputDocument + */ + private function buildInitializeInput() + { + return $this->isOrm() ? new InitializeInput() : new InitializeInputDocument(); + } } diff --git a/features/jsonld/input_output.feature b/features/jsonld/input_output.feature index a4be765cead..b476252fafe 100644 --- a/features/jsonld/input_output.feature +++ b/features/jsonld/input_output.feature @@ -317,3 +317,26 @@ Feature: JSON-LD DTO input and output "data": 123 } """ + + @createSchema + Scenario: Initialize input data with a DataTransformerInitializer + Given there is an InitializeInput object with id 1 + When I send a "PUT" request to "/initialize_inputs/1" with body: + """ + { + "name": "La peste" + } + """ + Then the response status code should be 200 + And the response should be in JSON + And the JSON should be equal to: + """ + { + "@context": "/contexts/InitializeInput", + "@id": "/initialize_inputs/1", + "@type": "InitializeInput", + "id": 1, + "manager": "Orwell", + "name": "La peste" + } + """ diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index fb8439aa44d..bee47615eaa 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -30,6 +30,7 @@ use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface; use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; use ApiPlatform\Core\DataProvider\SubresourceDataProviderInterface; +use ApiPlatform\Core\DataTransformer\DataTransformerInitializerInterface; use ApiPlatform\Core\DataTransformer\DataTransformerInterface; use ApiPlatform\Core\GraphQl\Error\ErrorHandlerInterface; use ApiPlatform\Core\GraphQl\Resolver\MutationResolverInterface; diff --git a/src/DataTransformer/DataTransformerInitializerInterface.php b/src/DataTransformer/DataTransformerInitializerInterface.php index 5e0687700e1..85007c818e1 100644 --- a/src/DataTransformer/DataTransformerInitializerInterface.php +++ b/src/DataTransformer/DataTransformerInitializerInterface.php @@ -19,6 +19,8 @@ interface DataTransformerInitializerInterface extends DataTransformerInterface * Creates a new DTO object that the data will then be serialized into (using object_to_populate). * * This is useful to "initialize" the DTO object based on the current resource's data. + * + * @return object|null */ - public function initialize(string $inputClass, array $context = []): ?object; + public function initialize(string $inputClass, array $context = []); } diff --git a/tests/Fixtures/TestBundle/DataTransformer/InitializeInputDataTransformer.php b/tests/Fixtures/TestBundle/DataTransformer/InitializeInputDataTransformer.php new file mode 100644 index 00000000000..9f151b0a0c5 --- /dev/null +++ b/tests/Fixtures/TestBundle/DataTransformer/InitializeInputDataTransformer.php @@ -0,0 +1,62 @@ + + * + * 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\Core\Tests\Fixtures\TestBundle\DataTransformer; + +use ApiPlatform\Core\DataTransformer\DataTransformerInitializerInterface; +use ApiPlatform\Core\Serializer\AbstractItemNormalizer; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\InitializeInput as InitializeInputDocument; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\InitializeInputDto; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\InitializeInput; + +final class InitializeInputDataTransformer implements DataTransformerInitializerInterface +{ + /** + * {@inheritdoc} + */ + public function transform($object, string $to, array $context = []) + { + /** @var InitializeInputDto */ + $data = $object; + + /** @var InitializeInput|InitializeInputDocument */ + $resourceObject = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE] ?? new $context['resource_class'](); + $resourceObject->name = $data->name; + + return $resourceObject; + } + + /** + * {@inheritdoc} + */ + public function initialize(string $inputClass, array $context = []) + { + $currentResource = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE] ?? null; + if (!$currentResource) { + return new InitializeInputDto(); + } + + $dto = new InitializeInputDto(); + $dto->manager = $currentResource->manager; + + return $dto; + } + + /** + * {@inheritdoc} + */ + public function supportsTransformation($data, string $to, array $context = []): bool + { + return (InitializeInput::class === $to || InitializeInputDocument::class === $to) && InitializeInputDto::class === ($context['input']['class'] ?? null); + } +} diff --git a/tests/Fixtures/TestBundle/Document/InitializeInput.php b/tests/Fixtures/TestBundle/Document/InitializeInput.php new file mode 100644 index 00000000000..3171097a946 --- /dev/null +++ b/tests/Fixtures/TestBundle/Document/InitializeInput.php @@ -0,0 +1,40 @@ + + * + * 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\Core\Tests\Fixtures\TestBundle\Document; + +use ApiPlatform\Core\Annotation\ApiResource; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\InitializeInputDto; +use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; + +/** + * @ApiResource(input=InitializeInputDto::class) + * @ODM\Document + */ +class InitializeInput +{ + /** + * @ODM\Id(strategy="NONE", type="integer") + */ + public $id; + + /** + * @ODM\Field + */ + public $manager; + + /** + * @ODM\Field + */ + public $name; +} diff --git a/tests/Fixtures/TestBundle/Dto/InitializeInputDto.php b/tests/Fixtures/TestBundle/Dto/InitializeInputDto.php new file mode 100644 index 00000000000..a7fa3c8dc94 --- /dev/null +++ b/tests/Fixtures/TestBundle/Dto/InitializeInputDto.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\Core\Tests\Fixtures\TestBundle\Dto; + +class InitializeInputDto +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $manager; +} diff --git a/tests/Fixtures/TestBundle/Entity/InitializeInput.php b/tests/Fixtures/TestBundle/Entity/InitializeInput.php new file mode 100644 index 00000000000..07e77f7d486 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/InitializeInput.php @@ -0,0 +1,41 @@ + + * + * 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\Core\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiResource; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\InitializeInputDto; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ApiResource(input=InitializeInputDto::class) + * @ORM\Entity + */ +class InitializeInput +{ + /** + * @ORM\Id + * @ORM\Column(type="integer") + */ + public $id; + + /** + * @ORM\Column + */ + public $manager; + + /** + * @ORM\Column + */ + public $name; +} diff --git a/tests/Fixtures/app/config/config_common.yml b/tests/Fixtures/app/config/config_common.yml index 5736d354ac9..68b5f801f52 100644 --- a/tests/Fixtures/app/config/config_common.yml +++ b/tests/Fixtures/app/config/config_common.yml @@ -291,6 +291,12 @@ services: tags: - { name: 'api_platform.data_transformer' } + app.data_transformer.initialize_input: + class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\DataTransformer\InitializeInputDataTransformer' + public: false + tags: + - { name: 'api_platform.data_transformer' } + app.messenger_handler.messenger_with_response: class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\MessengerHandler\MessengerWithResponseHandler' public: false diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php index 4120c465542..7f81ac608df 100644 --- a/tests/Serializer/AbstractItemNormalizerTest.php +++ b/tests/Serializer/AbstractItemNormalizerTest.php @@ -429,7 +429,7 @@ public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); $resourceClassResolverProphecy->getResourceClass(null, DummyForAdditionalFields::class)->willReturn(DummyForAdditionalFields::class); - $inputDataTransformerProphecy = $this->prophesize(DataTransformerInterface::class); + $inputDataTransformerProphecy = $this->prophesize(DataTransformerInitializerInterface::class); $inputDataTransformerProphecy->willImplement(DataTransformerInitializerInterface::class); $inputDataTransformerProphecy->initialize(DummyForAdditionalFieldsInput::class, $cleanedContext)->willReturn($preHydratedDummy); $inputDataTransformerProphecy->supportsTransformation($data, DummyForAdditionalFields::class, $augmentedContext)->willReturn(true);