[HttpKernel] Add argument $mapWhenEmpty to MapQueryString and MapRequestPayload for always attempting denormalization with empty query and request payload

This commit is contained in:
Jeroeny
2023-10-18 11:34:36 +02:00
committed by Nicolas Grekas
parent e467d6bfc9
commit 5117bb6f17
5 changed files with 72 additions and 3 deletions

View File

@@ -41,6 +41,7 @@ class MapQueryString extends ValueResolver
string $resolver = RequestPayloadValueResolver::class,
public readonly int $validationFailedStatusCode = Response::HTTP_NOT_FOUND,
public readonly ?string $key = null,
public bool $mapWhenEmpty = false,
) {
parent::__construct($resolver);
}

View File

@@ -44,6 +44,7 @@ class MapRequestPayload extends ValueResolver
string $resolver = RequestPayloadValueResolver::class,
public readonly int $validationFailedStatusCode = Response::HTTP_UNPROCESSABLE_ENTITY,
public readonly ?string $type = null,
public bool $mapWhenEmpty = false,
) {
parent::__construct($resolver);
}

View File

@@ -23,6 +23,7 @@ CHANGELOG
* Deprecate passing a `ControllerArgumentsEvent` to the `ViewEvent` constructor; pass a `ControllerArgumentsMetadata` instead
* Support variadic argument with `#[MapRequestPayload]`
* Add `#[Serialize]` to serialize values returned by controllers
* Add argument `$mapWhenEmpty` to `MapQueryString` and `MapRequestPayload` for always attempting denormalization with empty query and request payload
8.0
---

View File

@@ -202,7 +202,7 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
private function mapQueryString(Request $request, ArgumentMetadata $argument, MapQueryString $attribute): ?object
{
if (!($data = $request->query->all($attribute->key)) && ($argument->isNullable() || $argument->hasDefaultValue())) {
if (!($data = $request->query->all($attribute->key)) && ($argument->isNullable() || $argument->hasDefaultValue()) && !$attribute->mapWhenEmpty) {
return null;
}
@@ -211,8 +211,12 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
private function mapRequestPayload(Request $request, ArgumentMetadata $argument, MapRequestPayload $attribute): object|array|null
{
if ('' === ($data = $request->request->all() ?: $request->getContent()) && ($argument->isNullable() || $argument->hasDefaultValue())) {
return null;
if ('' === $data = $request->request->all() ?: $request->getContent()) {
if ($attribute->mapWhenEmpty) {
$data = [];
} elseif ($argument->isNullable() || $argument->hasDefaultValue()) {
return null;
}
}
if (null === $format = $request->getContentTypeFormat()) {

View File

@@ -165,6 +165,46 @@ class RequestPayloadValueResolverTest extends TestCase
$this->assertSame([null], $event->getArguments());
}
public function testMapQueryStringEmpty()
{
$payload = new RequestPayload(50);
$denormalizer = new RequestPayloadDenormalizer($payload);
$serializer = new Serializer([$denormalizer]);
$resolver = new RequestPayloadValueResolver($serializer);
$argument = new ArgumentMetadata('valid', RequestPayload::class, false, false, null, false, [
MapQueryString::class => new MapQueryString(mapWhenEmpty: true),
]);
$request = Request::create('/', 'GET');
$kernel = $this->createStub(HttpKernelInterface::class);
$arguments = $resolver->resolve($request, $argument);
$event = new ControllerArgumentsEvent($kernel, static fn () => null, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
$resolver->onKernelControllerArguments($event);
$this->assertSame([$payload], $event->getArguments());
}
public function testMapRequestPayloadEmpty()
{
$payload = new RequestPayload(50);
$denormalizer = new RequestPayloadDenormalizer($payload);
$serializer = new Serializer([$denormalizer]);
$resolver = new RequestPayloadValueResolver($serializer);
$argument = new ArgumentMetadata('valid', RequestPayload::class, false, false, null, false, [
MapRequestPayload::class => new MapRequestPayload(mapWhenEmpty: true),
]);
$request = Request::create('/', 'POST');
$kernel = $this->createStub(HttpKernelInterface::class);
$arguments = $resolver->resolve($request, $argument);
$event = new ControllerArgumentsEvent($kernel, static fn () => null, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
$resolver->onKernelControllerArguments($event);
$this->assertSame([$payload], $event->getArguments());
}
public function testNullPayloadAndNotDefaultOrNullableArgument()
{
$validator = $this->createMock(ValidatorInterface::class);
@@ -1415,3 +1455,25 @@ class FormPayloadWithBool
{
}
}
class RequestPayloadDenormalizer implements DenormalizerInterface
{
public function __construct(private RequestPayload $payload)
{
}
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): mixed
{
return $this->payload;
}
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
{
return RequestPayload::class === $type;
}
public function getSupportedTypes(?string $format = null): array
{
return [RequestPayload::class => true];
}
}