8000 map a list of items with MapRequestPayload attribute · symfony/symfony@ef669f4 · GitHub
[go: up one dir, main page]

Skip to content

Commit ef669f4

Browse files
committed
map a list of items with MapRequestPayload attribute
1 parent b5ee977 commit ef669f4

File tree

4 files changed

+43
-1
lines changed

4 files changed

+43
-1
lines changed

src/Symfony/Component/HttpKernel/Attribute/MapRequestPayload.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ class MapRequestPayload extends ValueResolver
3232
* @param string|GroupSequence|array<string>|null $validationGroups The validation groups to use when validating the query string mapping
3333
* @param class-string $resolver The class name of the resolver to use
3434
* @param int $validationFailedStatusCode The HTTP code to return if the validation fails
35+
* @param class-string|string|null $itemsType The element type for array deserialization.
3536
*/
3637
public function __construct(
3738
public readonly array|string|null $acceptFormat = null,
3839
public readonly array $serializationContext = [],
3940
public readonly string|GroupSequence|array|null $validationGroups = null,
4041
string $resolver = RequestPayloadValueResolver::class,
4142
public readonly int $validationFailedStatusCode = Response::HTTP_UNPROCESSABLE_ENTITY,
43+
public readonly ?string $itemsType = null,
4244
) {
4345
parent::__construct($resolver);
4446
}

src/Symfony/Component/HttpKernel/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Add `HttpException::fromStatusCode()`
99
* Add `$validationFailedStatusCode` argument to `#[MapQueryParameter]` that allows setting a custom HTTP status code when validation fails
1010
* Add `NearMissValueResolverException` to let value resolvers report when an argument could be under their watch but failed to be resolved
11+
* Add `$itemsType` argument to `#[MapRequestPayload]` that allows mapping a list of items
1112

1213
7.0
1314
---

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ private function mapQueryString(Request $request, string $type, MapQueryString $
170170
return $this->serializer->denormalize($data, $type, null, $attribute->serializationContext + self::CONTEXT_DENORMALIZE + ['filter_bool' => true]);
171171
}
172172

173-
private function mapRequestPayload(Request $request, string $type, MapRequestPayload $attribute): ?object
173+
private function mapRequestPayload(Request $request, string $type, MapRequestPayload $attribute): object|array|null
174174
{
175175
if (null === $format = $request->getContentTypeFormat()) {
176176
throw new UnsupportedMediaTypeHttpException('Unsupported format.');
@@ -180,6 +180,10 @@ private function mapRequestPayload(Request $request, string $type, MapRequestPay
180180
throw new UnsupportedMediaTypeHttpException(sprintf('Unsupported format, expects "%s", but "%s" given.', implode('", "', (array) $attribute->acceptFormat), $format));
181181
}
182182

183+
if ('array' === $type && null !== $attribute->itemsType) {
184+
$type = $attribute->itemsType.'[]';
185+
}
186+
183187
if ($data = $request->request->all()) {
184188
return $this->serializer->denormalize($data, $type, null, $attribute->serializationContext + self::CONTEXT_DENORMALIZE + ('form' === $format ? ['filter_bool' => true] : []));
185189
}

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\Component\Serializer\Encoder\XmlEncoder;
2626
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2727
use Symfony\Component\Serializer\Exception\PartialDenormalizationException;
28+
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
2829
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
2930
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
3031
use Symfony\Component\Serializer\Serializer;
@@ -421,6 +422,40 @@ public function testRequestInputValidationPassed()
421422
$this->assertEquals([$payload], $event->getArguments());
422423
}
423424

425+
public function testRequestArrayDenormalization()
426+
{
427+
$input = [
428+
['price' => '50'],
429+
['price' => '23'],
430+
];
431+
$payload = [
432+
new RequestPayload(50),
433+
new RequestPayload(23),
434+
];
435+
436+
$serializer = new Serializer([new ArrayDenormalizer(), new ObjectNormalizer()], ['json' => new JsonEncoder()]);
437+
438+
$validator = $this->createMock(ValidatorInterface::class);
439+
$validator->expects($this->once())
440+
->method('validate')
441+
->willReturn(new ConstraintViolationList());
442+
443+
$resolver = new RequestPayloadValueResolver($serializer, $validator);
444+
445+
$argument = new ArgumentMetadata('prices', 'array', false, false, null, false, [
446+
MapRequestPayload::class => new MapRequestPayload(itemsType: RequestPayload::class),
447+
]);
448+
$request = Request::create('/', 'POST', $input);
449+
450+
$kernel = $this->createMock(HttpKernelInterface::class);
451+
$arguments = $resolver->resolve($request, $argument);
452+
$event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
453+
454+
$resolver->onKernelControllerArguments($event);
455+
456+
$this->assertEquals([$payload], $event->getArguments());
457+
}
458+
424459
public function testItThrowsOnVariadicArgument()
425460
{
426461
$serializer = new Serializer();

0 commit comments

Comments
 (0)
0