diff --git a/src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php b/src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php index 411937a4f04fc..f3717b31d0628 100644 --- a/src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php +++ b/src/Symfony/Component/HttpKernel/Attribute/MapQueryString.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\Attribute; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver; +use Symfony\Component\Validator\Constraints\GroupSequence; /** * Controller parameter tag to map the query string of the request to typed object and validate it. @@ -22,7 +23,8 @@ class MapQueryString extends ValueResolver { public function __construct( - public readonly array $context = [], + public readonly array $serializationContext = [], + public readonly string|GroupSequence|array|null $validationGroups = null, string $resolver = RequestPayloadValueResolver::class, ) { parent::__construct($resolver); diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php index f73c9a58d6ee6..89a07c19f5ca9 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php @@ -120,7 +120,7 @@ private function mapQueryString(Request $request, string $type, MapQueryString $ return null; } - return $this->serializer->denormalize($data, $type, null, self::CONTEXT_DENORMALIZE + $attribute->context); + return $this->serializer->denormalize($data, $type, null, self::CONTEXT_DENORMALIZE + $attribute->serializationContext); } private function mapRequestPayload(Request $request, string $type, MapRequestPayload $attribute): ?object diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php index 078ee02a216f8..ecfaa3e0f621d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\MapQueryString; use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; +use Symfony\Component\HttpKernel\Attribute\ValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\HttpException; @@ -312,7 +313,7 @@ public static function provideMismatchedFormatContext(): iterable /** * @dataProvider provideValidationGroupsOnManyTypes */ - public function testValidationGroupsPassed(mixed $groups) + public function testValidationGroupsPassed(string $method, ValueResolver $attribute) { $input = ['price' => '50', 'title' => 'A long title, so the validation passes']; @@ -323,10 +324,10 @@ public function testValidationGroupsPassed(mixed $groups) $validator = (new ValidatorBuilder())->enableAnnotationMapping()->getValidator(); $resolver = new RequestPayloadValueResolver($serializer, $validator); - $request = Request::create('/', 'POST', $input); + $request = Request::create('/', $method, $input); $argument = new ArgumentMetadata('valid', RequestPayload::class, false, false, null, false, [ - MapRequestPayload::class => new MapRequestPayload(validationGroups: $groups), + $attribute::class => $attribute, ]); $resolved = $resolver->resolve($request, $argument); @@ -338,7 +339,7 @@ public function testValidationGroupsPassed(mixed $groups) /** * @dataProvider provideValidationGroupsOnManyTypes */ - public function testValidationGroupsNotPassed(mixed $groups) + public function testValidationGroupsNotPassed(string $method, ValueResolver $attribute) { $input = ['price' => '50', 'title' => 'Too short']; @@ -347,9 +348,9 @@ public function testValidationGroupsNotPassed(mixed $groups) $resolver = new RequestPayloadValueResolver($serializer, $validator); $argument = new ArgumentMetadata('valid', RequestPayload::class, false, false, null, false, [ - MapRequestPayload::class => new MapRequestPayload(validationGroups: $groups), + $attribute::class => $attribute, ]); - $request = Request::create('/', 'POST', $input); + $request = Request::create('/', $method, $input); try { $resolver->resolve($request, $argument); @@ -364,11 +365,35 @@ public function testValidationGroupsNotPassed(mixed $groups) public static function provideValidationGroupsOnManyTypes(): iterable { - yield 'validation group as string' => ['strict']; + yield 'request payload with validation group as string' => [ + 'POST', + new MapRequestPayload(validationGroups: 'strict'), + ]; - yield 'validation group as array' => [['strict']]; + yield 'request payload with validation group as array' => [ + 'POST', + new MapRequestPayload(validationGroups: ['strict']), + ]; + + yield 'request payload with validation group as GroupSequence' => [ + 'POST', + new MapRequestPayload(validationGroups: new Assert\GroupSequence(['strict'])), + ]; - yield 'validation group as GroupSequence' => [new Assert\GroupSequence(['strict'])]; + yield 'query with validation group as string' => [ + 'GET', + new MapQueryString(validationGroups: 'strict'), + ]; + + yield 'query with validation group as array' => [ + 'GET', + new MapQueryString(validationGroups: ['strict']), + ]; + + yield 'query with validation group as GroupSequence' => [ + 'GET', + new MapQueryString(validationGroups: new Assert\GroupSequence(['strict'])), + ]; } }