Skip to content

Commit c7892a6

Browse files
fix(serializer): retrieve only first uriVariable from operation (#5788)
* fix(serializer): retrieve only first uriVariable from operation * array default --------- Co-authored-by: Antoine Bluchet <[email protected]>
1 parent c14b6f4 commit c7892a6

File tree

2 files changed

+14
-83
lines changed

2 files changed

+14
-83
lines changed

src/Serializer/ItemNormalizer.php

+14-12
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use ApiPlatform\Api\ResourceClassResolverInterface;
1818
use ApiPlatform\Api\UrlGeneratorInterface;
1919
use ApiPlatform\Exception\InvalidArgumentException;
20-
use ApiPlatform\Metadata\Link;
20+
use ApiPlatform\Metadata\HttpOperation;
2121
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2222
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2323
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
@@ -77,6 +77,13 @@ private function updateObjectToPopulate(array $data, array &$context): void
7777
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri((string) $data['id'], $context + ['fetch_data' => true]);
7878
} catch (InvalidArgumentException) {
7979
$operation = $this->resourceMetadataCollectionFactory->create($context['resource_class'])->getOperation();
80+
if (
81+
null !== ($context['uri_variables'] ?? null)
82+
&& $operation instanceof HttpOperation
83+
&& \count($operation->getUriVariables() ?? []) > 1
84+
) {
85+
throw new InvalidArgumentException('Cannot find object to populate, use JSON-LD or specify an IRI at path "id".');
86+
}
8087
$uriVariables = $this->getContextUriVariables($data, $operation, $context);
8188
$iri = $this->iriConverter->getIriFromResource($context['resource_class'], UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => $uriVariables]);
8289

@@ -86,18 +93,13 @@ private function updateObjectToPopulate(array $data, array &$context): void
8693

8794
private function getContextUriVariables(array $data, $operation, array $context): array
8895
{
89-
$uriVariables = $context['uri_variables'] ?? $data;
90-
91-
/** @var Link $uriVariable */
92-
foreach ($operation->getUriVariables() as $uriVariable) {
93-
if (isset($uriVariables[$uriVariable->getParameterName()])) {
94-
continue;
95-
}
96+
$uriVariables = $context['uri_variables'] ?? [];
9697

97-
foreach ($uriVariable->getIdentifiers() as $identifier) {
98-
if (isset($data[$identifier])) {
99-
$uriVariables[$uriVariable->getParameterName()] = $data[$identifier];
100-
}
98+
$operationUriVariables = $operation->getUriVariables();
99+
if ((null !== $uriVariable = array_shift($operationUriVariables)) && \count($uriVariable->getIdentifiers())) {
100+
$identifier = $uriVariable->getIdentifiers()[0];
101+
if (isset($data[$identifier])) {
102+
$uriVariables[$uriVariable->getParameterName()] = $data[$identifier];
101103
}
102104
}
103105

tests/Serializer/ItemNormalizerTest.php

-71
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,10 @@
1515

1616
use ApiPlatform\Api\IriConverterInterface;
1717
use ApiPlatform\Api\ResourceClassResolverInterface;
18-
use ApiPlatform\Api\UrlGeneratorInterface;
19-
use ApiPlatform\Exception\InvalidArgumentException;
2018
use ApiPlatform\Metadata\ApiProperty;
21-
use ApiPlatform\Metadata\ApiResource;
22-
use ApiPlatform\Metadata\Get;
23-
use ApiPlatform\Metadata\Link;
24-
use ApiPlatform\Metadata\Operations;
2519
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2620
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2721
use ApiPlatform\Metadata\Property\PropertyNameCollection;
28-
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
29-
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
3022
use ApiPlatform\Serializer\ItemNormalizer;
3123
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
3224
use PHPUnit\Framework\TestCase;
@@ -189,69 +181,6 @@ public function testDenormalizeWithIri(): void
189181
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['id' => '/dummies/12', 'name' => 'hello'], Dummy::class, null, $context));
190182
}
191183

192-
public function testDenormalizeGuessingUriVariables(): void
193-
{
194-
$context = ['resource_class' => Dummy::class, 'api_allow_update' => true, 'uri_variables' => [
195-
'parent_resource' => '1',
196-
'resource' => '1',
197-
]];
198-
199-
$propertyNameCollection = new PropertyNameCollection(['name']);
200-
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
201-
$propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::cetera())->willReturn($propertyNameCollection)->shouldBeCalled();
202-
203-
$propertyMetadata = (new ApiProperty())->withReadable(true)->withWritable(true);
204-
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
205-
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::cetera())->willReturn($propertyMetadata)->shouldBeCalled();
206-
207-
$uriVariables = [
208-
'parent_resource' => new Link('parent_resource', identifiers: ['id']),
209-
'resource' => new Link('resource', identifiers: ['id']),
210-
'sub_resource' => new Link('sub_resource', identifiers: ['id']),
211-
];
212-
$resourceMetadataCollectionFactoryProphecy = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
213-
$resourceMetadataCollectionFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadataCollection(Dummy::class, [
214-
(new ApiResource())->withShortName('Dummy')->withOperations(new Operations([
215-
'sub_resource' => (new Get(uriVariables: $uriVariables))->withShortName('Dummy'),
216-
])),
217-
]));
218-
219-
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
220-
$resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
221-
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
222-
223-
$serializerProphecy = $this->prophesize(SerializerInterface::class);
224-
$serializerProphecy->willImplement(DenormalizerInterface::class);
225-
226-
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
227-
$iriConverterProphecy->getResourceFromIri(Argument::is('12'), Argument::cetera())->willThrow(InvalidArgumentException::class);
228-
$iriConverterProphecy
229-
->getIriFromResource(
230-
Dummy::class,
231-
UrlGeneratorInterface::ABS_PATH,
232-
Argument::type(Get::class),
233-
Argument::withEntry('uri_variables', Argument::allOf(
234-
Argument::withEntry('parent_resource', '1'),
235-
Argument::withEntry('resource', '1'),
236-
Argument::withEntry('sub_resource', '12')
237-
))
238-
)
239-
->willReturn('parent_resource/1/resource/1/sub_resource/2')
240-
->shouldBeCalledOnce();
241-
$iriConverterProphecy->getResourceFromIri('parent_resource/1/resource/1/sub_resource/2', ['fetch_data' => true])->shouldBeCalledOnce();
242-
243-
$normalizer = new ItemNormalizer(
244-
$propertyNameCollectionFactoryProphecy->reveal(),
245-
$propertyMetadataFactoryProphecy->reveal(),
246-
$iriConverterProphecy->reveal(),
247-
$resourceClassResolverProphecy->reveal(),
248-
resourceMetadataFactory: $resourceMetadataCollectionFactoryProphecy->reveal(),
249-
);
250-
$normalizer->setSerializer($serializerProphecy->reveal());
251-
252-
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['id' => '12', 'name' => 'hello'], Dummy::class, null, $context));
253-
}
254-
255184
public function testDenormalizeWithIdAndUpdateNotAllowed(): void
256185
{
257186
$this->expectException(NotNormalizableValueException::class);

0 commit comments

Comments
 (0)