Skip to content

Commit 5335963

Browse files
authored
Merge pull request #1899 from antograssiot/fix-identifier-casting
[Bug] Fix integer Identifiers denormalization
2 parents 1b650ee + 1ea6f68 commit 5335963

File tree

8 files changed

+72
-8
lines changed

8 files changed

+72
-8
lines changed

features/bootstrap/FeatureContext.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,7 @@ public function thereIsARelatedDummyWithFriends(int $nb)
733733
$relatedDummy2->setName('RelatedDummy without friends');
734734
$this->manager->persist($relatedDummy2);
735735
$this->manager->flush();
736+
$this->manager->clear();
736737
}
737738

738739
/**

features/doctrine/search_filter.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Feature: Search filter on collections
1010
And I send a "GET" request to "/related_dummies?relatedToDummyFriend.dummyFriend=/dummy_friends/4"
1111
Then the response status code should be 200
1212
And the JSON node "_embedded.item" should have 1 element
13+
And the JSON node "_embedded.item[0].id" should be equal to the number 1
1314
And the JSON node "_embedded.item[0]._links.relatedToDummyFriend" should have 4 elements
1415
And the JSON node "_embedded.item[0]._embedded.relatedToDummyFriend" should have 4 elements
1516

features/main/non_resource.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Feature: Non-resources handling
1111
"@context": "/contexts/ContainNonResource",
1212
"@id": "/contain_non_resources/1",
1313
"@type": "ContainNonResource",
14-
"id": "1",
14+
"id": 1,
1515
"nested": {
1616
"@id": "/contain_non_resources/1-nested",
1717
"@type": "ContainNonResource",

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,10 @@
236236
<argument type="tagged" tag="api_platform.identifier.denormalizer" />
237237
</service>
238238

239+
<service id="api_platform.identifier.integer" class="ApiPlatform\Core\Identifier\Normalizer\IntegerDenormalizer" public="false">
240+
<tag name="api_platform.identifier.denormalizer" />
241+
</service>
242+
239243
<service id="api_platform.identifier.date_normalizer" class="ApiPlatform\Core\Identifier\Normalizer\DateTimeIdentifierDenormalizer" public="false">
240244
<tag name="api_platform.identifier.denormalizer" />
241245
</service>

src/Identifier/Normalizer/ChainIdentifierDenormalizer.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,8 @@ public function denormalize($data, $class, $format = null, array $context = [])
6262
throw new InvalidIdentifierException(sprintf('Invalid identifier "%1$s", "%1$s" was not found.', $key));
6363
}
6464

65+
$metadata = $this->getIdentifierMetadata($class, $key);
6566
foreach ($this->identifierDenormalizers as $normalizer) {
66-
$metadata = $this->getIdentifierMetadata($class, $key);
67-
6867
if (!$normalizer->supportsDenormalization($identifiers[$key], $metadata)) {
6968
continue;
7069
}
@@ -82,8 +81,10 @@ public function denormalize($data, $class, $format = null, array $context = [])
8281

8382
private function getIdentifierMetadata($class, $propertyName)
8483
{
85-
$type = $this->propertyMetadataFactory->create($class, $propertyName)->getType();
84+
if (!$type = $this->propertyMetadataFactory->create($class, $propertyName)->getType()) {
85+
return null;
86+
}
8687

87-
return $type && Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType() ? $type->getClassName() : null;
88+
return Type::BUILTIN_TYPE_OBJECT === ($builtInType = $type->getBuiltinType()) ? $type->getClassName() : $builtInType;
8889
}
8990
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Identifier\Normalizer;
15+
16+
use Symfony\Component\PropertyInfo\Type;
17+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
18+
19+
final class IntegerDenormalizer implements DenormalizerInterface
20+
{
21+
public function denormalize($data, $class, $format = null, array $context = []): int
22+
{
23+
return (int) $data;
24+
}
25+
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
public function supportsDenormalization($data, $type, $format = null): bool
30+
{
31+
return Type::BUILTIN_TYPE_INT === $type && \is_string($data);
32+
}
33+
}

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ private function getPartialContainerBuilderProphecy($test = false)
500500
'api_platform.filters',
501501
'api_platform.iri_converter',
502502
'api_platform.identifier.denormalizer',
503+
'api_platform.identifier.integer',
503504
'api_platform.identifier.date_normalizer',
504505
'api_platform.identifier.uuid_normalizer',
505506
'api_platform.identifiers_extractor',

tests/Identifier/Normalizer/ChainIdentifierDenormalizerTest.php

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Core\Api\IdentifiersExtractorInterface;
1717
use ApiPlatform\Core\Identifier\Normalizer\ChainIdentifierDenormalizer;
1818
use ApiPlatform\Core\Identifier\Normalizer\DateTimeIdentifierDenormalizer;
19+
use ApiPlatform\Core\Identifier\Normalizer\IntegerDenormalizer;
1920
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2021
use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
2122
use PHPUnit\Framework\TestCase;
@@ -31,22 +32,25 @@ public function testCompositeIdentifier()
3132
$identifier = 'a=1;c=2;d=2015-04-05';
3233
$class = 'Dummy';
3334

35+
$integerPropertyMetadata = (new PropertyMetadata())->withIdentifier(true)->withType(new Type(Type::BUILTIN_TYPE_INT));
3436
$identifierPropertyMetadata = (new PropertyMetadata())->withIdentifier(true);
3537
$dateIdentifierPropertyMetadata = (new PropertyMetadata())->withIdentifier(true)->withType(new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTime::class));
3638

3739
$propertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
38-
$propertyMetadataFactory->create($class, 'a')->shouldBeCalled()->willReturn($identifierPropertyMetadata);
40+
$propertyMetadataFactory->create($class, 'a')->shouldBeCalled()->willReturn($integerPropertyMetadata);
3941
$propertyMetadataFactory->create($class, 'c')->shouldBeCalled()->willReturn($identifierPropertyMetadata);
4042
$propertyMetadataFactory->create($class, 'd')->shouldBeCalled()->willReturn($dateIdentifierPropertyMetadata);
4143

4244
$identifiersExtractor = $this->prophesize(IdentifiersExtractorInterface::class);
4345
$identifiersExtractor->getIdentifiersFromResourceClass($class)->willReturn(['a', 'c', 'd']);
4446

45-
$identifierDenormalizers = [new DateTimeIdentifierDenormalizer()];
47+
$identifierDenormalizers = [new IntegerDenormalizer(), new DateTimeIdentifierDenormalizer()];
4648

4749
$identifierDenormalizer = new ChainIdentifierDenormalizer($identifiersExtractor->reveal(), $propertyMetadataFactory->reveal(), $identifierDenormalizers);
4850

49-
$this->assertEquals($identifierDenormalizer->denormalize($identifier, $class), ['a' => '1', 'c' => '2', 'd' => new \DateTime('2015-04-05')]);
51+
$result = $identifierDenormalizer->denormalize($identifier, $class);
52+
$this->assertEquals(['a' => 1, 'c' => '2', 'd' => new \DateTime('2015-04-05')], $result);
53+
$this->assertSame(1, $result['a']);
5054
}
5155

5256
public function testSingleDateIdentifier()
@@ -67,4 +71,23 @@ public function testSingleDateIdentifier()
6771

6872
$this->assertEquals($identifierDenormalizer->denormalize($identifier, $class), ['funkyid' => new \DateTime('2015-04-05')]);
6973
}
74+
75+
public function testIntegerIdentifier()
76+
{
77+
$identifier = '42';
78+
$class = 'Dummy';
79+
80+
$integerIdentifierPropertyMetadata = (new PropertyMetadata())->withIdentifier(true)->withType(new Type(Type::BUILTIN_TYPE_INT));
81+
82+
$propertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class);
83+
$propertyMetadataFactory->create($class, 'id')->shouldBeCalled()->willReturn($integerIdentifierPropertyMetadata);
84+
85+
$identifiersExtractor = $this->prophesize(IdentifiersExtractorInterface::class);
86+
$identifiersExtractor->getIdentifiersFromResourceClass($class)->willReturn(['id']);
87+
88+
$identifierDenormalizers = [new IntegerDenormalizer()];
89+
$identifierDenormalizer = new ChainIdentifierDenormalizer($identifiersExtractor->reveal(), $propertyMetadataFactory->reveal(), $identifierDenormalizers);
90+
91+
$this->assertSame(['id' => 42], $identifierDenormalizer->denormalize($identifier, $class));
92+
}
7093
}

0 commit comments

Comments
 (0)