From 391e9df25698663b964e1adc36c8f3bbc2e1cf61 Mon Sep 17 00:00:00 2001 From: RJ Garcia Date: Sun, 3 Feb 2019 01:13:50 -0800 Subject: [PATCH] Denormalize Input Class with Different Fields Fixes the bug where AbstractItemSerializer would use the resource class instead of the input class to determine allowed properties. Enforced `$context['resource_class']` to be set to current denormalized class to resolve issue. Figured this should be the case since AbstractItemNormalizer only works on resources. Signed-off-by: RJ Garcia --- src/Serializer/AbstractItemNormalizer.php | 4 +- .../Entity/DummyForAdditionalFields.php | 56 +++++++++++++++++++ .../Entity/DummyForAdditionalFieldsInput.php | 34 +++++++++++ .../Serializer/AbstractItemNormalizerTest.php | 44 +++++++++++++++ 4 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 tests/Fixtures/TestBundle/Entity/DummyForAdditionalFields.php create mode 100644 tests/Fixtures/TestBundle/Entity/DummyForAdditionalFieldsInput.php diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index 4e5c2ca79b1..950fe22866a 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -128,9 +128,7 @@ public function supportsDenormalization($data, $type, $format = null) public function denormalize($data, $class, $format = null, array $context = []) { $context['api_denormalize'] = true; - if (!isset($context['resource_class'])) { - $context['resource_class'] = $class; - } + $context['resource_class'] = $class; return parent::denormalize($data, $class, $format, $context); } diff --git a/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFields.php b/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFields.php new file mode 100644 index 00000000000..4031ac8ff0c --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFields.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\Core\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiResource; +use Doctrine\ORM\Mapping as ORM; + +/** + * @ApiResource + * @ORM\Entity + */ +class DummyForAdditionalFields +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + /** @ORM\Column */ + private $name; + /** @ORM\Column */ + private $slug; + + public function __construct(string $name, string $slug) + { + $this->name = $name; + $this->slug = $slug; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } + + public function getSlug(): string + { + return $this->slug; + } +} diff --git a/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFieldsInput.php b/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFieldsInput.php new file mode 100644 index 00000000000..58a061645ef --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/DummyForAdditionalFieldsInput.php @@ -0,0 +1,34 @@ + + * + * 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; + +/** + * @ApiResource + */ +final class DummyForAdditionalFieldsInput +{ + private $dummyName; + + public function __construct(string $dummyName) + { + $this->dummyName = $dummyName; + } + + public function getDummyName(): string + { + return $this->dummyName; + } +} diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php index 0d5fd6fb1f9..611087bc127 100644 --- a/tests/Serializer/AbstractItemNormalizerTest.php +++ b/tests/Serializer/AbstractItemNormalizerTest.php @@ -23,6 +23,8 @@ use ApiPlatform\Core\Metadata\Property\PropertyNameCollection; use ApiPlatform\Core\Serializer\AbstractItemNormalizer; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyForAdditionalFields; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyForAdditionalFieldsInput; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritance; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceChild; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\RelatedDummy; @@ -290,6 +292,48 @@ public function testDenormalize() ], Dummy::class); } + public function testCanDenormalizeInputClassWithDifferentFieldsThanResourceClass() + { + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + $propertyNameCollectionFactoryProphecy->create(DummyForAdditionalFieldsInput::class, [])->willReturn( + new PropertyNameCollection(['dummyName']) + ); + $propertyNameCollectionFactoryProphecy->create(DummyForAdditionalFields::class, [])->willReturn( + new PropertyNameCollection(['id', 'name', 'slug']) + ); + + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + // Create DummyForAdditionalFieldsInput mocks + $propertyMetadataFactoryProphecy->create(DummyForAdditionalFieldsInput::class, 'dummyName', [])->willReturn( + (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true) + ); + // Create DummyForAdditionalFields mocks + $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'id', [])->willReturn( + (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_INT), '', true, false))->withInitializable(false) + ); + $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'name', [])->willReturn( + (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true) + ); + $propertyMetadataFactoryProphecy->create(DummyForAdditionalFields::class, 'slug', [])->willReturn( + (new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true, false))->withInitializable(true) + ); + + $normalizer = new class($propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), $this->prophesize(IriConverterInterface::class)->reveal(), $this->prophesize(ResourceClassResolverInterface::class)->reveal()) extends AbstractItemNormalizer { + }; + + /** @var DummyForAdditionalFieldsInput $res */ + $res = $normalizer->denormalize([ + 'dummyName' => 'Dummy Name', + ], DummyForAdditionalFieldsInput::class, 'json', [ + 'resource_class' => DummyForAdditionalFields::class, + 'input_class' => DummyForAdditionalFieldsInput::class, + 'output_class' => DummyForAdditionalFields::class, + ]); + + $this->assertInstanceOf(DummyForAdditionalFieldsInput::class, $res); + $this->assertEquals('Dummy Name', $res->getDummyName()); + } + public function testDenormalizeWritableLinks() { $relatedDummy1 = new RelatedDummy();