From 2585de3967d75837250a51ebaee36fb5d259f090 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 15 Apr 2026 15:42:59 +0200 Subject: [PATCH 1/3] fix(validator): handle nested groups and group sequences --- .../ValidatorPropertyMetadataFactory.php | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php b/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php index 3fdc024188..8e23eebc31 100644 --- a/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php +++ b/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php @@ -156,16 +156,16 @@ private function getValidationGroups(ValidatorClassMetadataInterface $classMetad { if (isset($options['validation_groups'])) { if ($options['validation_groups'] instanceof GroupSequence) { - return $this->getGroupSequenceValidationGroups($options['validation_groups']); + return $this->flattenValidationGroups($options['validation_groups']->groups); } - if (!\is_callable($options['validation_groups'])) { - return $options['validation_groups']; + if (\is_array($options['validation_groups']) && !\is_callable($options['validation_groups'])) { + return $this->flattenValidationGroups($options['validation_groups']); } } if ($classMetadata->hasGroupSequence()) { - return $this->getGroupSequenceValidationGroups($classMetadata->getGroupSequence()); + return $this->flattenValidationGroups($classMetadata->getGroupSequence()->groups); } if (!method_exists($classMetadata, 'getDefaultGroup')) { @@ -176,22 +176,24 @@ private function getValidationGroups(ValidatorClassMetadataInterface $classMetad } /** - * @return array + * @param array $groups + * + * @return string[] */ - private function getGroupSequenceValidationGroups(GroupSequence $groupSequence): array + private function flattenValidationGroups(array $groups): array { - $groups = []; - foreach ($groupSequence->groups as $subGroup) { - if (\is_array($subGroup)) { - $groups[] = $subGroup; - } elseif ($subGroup instanceof GroupSequence) { - $groups[] = $this->getGroupSequenceValidationGroups($subGroup); + $flattenGroups = []; + foreach ($groups as $group) { + if (\is_array($group)) { + $flattenGroups[] = $this->flattenValidationGroups($group); + } elseif ($group instanceof GroupSequence) { + $flattenGroups[] = $this->flattenValidationGroups($group->groups); } else { - $groups[] = [$subGroup]; + $flattenGroups[] = [$group]; } } - return array_merge([], ...$groups); + return array_merge([], ...$flattenGroups); } /** From 6580c4ab4615c41d54e12528da5f9df0cb60669c Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 15 Apr 2026 15:50:19 +0200 Subject: [PATCH 2/3] Add test --- .../ValidatorPropertyMetadataFactoryTest.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Symfony/Tests/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php b/src/Symfony/Tests/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php index 29b0469497..3dbdc17d2a 100644 --- a/src/Symfony/Tests/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php +++ b/src/Symfony/Tests/Validator/Metadata/Property/ValidatorPropertyMetadataFactoryTest.php @@ -49,6 +49,7 @@ use Prophecy\PhpUnit\ProphecyTrait; use Symfony\Component\PropertyInfo\Type as LegacyType; use Symfony\Component\TypeInfo\Type; +use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\Hostname; use Symfony\Component\Validator\Constraints\Ulid; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -198,6 +199,29 @@ public function testCreateWithPropertyWithRightValidationGroupsAndRequiredConstr $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata); } + public function testCreateWithPropertyWithGroupSequenceValidationGroupsAndRequiredConstraints(): void + { + $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true); + $expectedPropertyMetadata = $propertyMetadata->withRequired(true); + + $groups = [new GroupSequence(['dummy'])]; + + $decoratedPropertyMetadataFactory = $this->prophesize(PropertyMetadataFactoryInterface::class); + $decoratedPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => $groups])->willReturn($propertyMetadata)->shouldBeCalled(); + + $validatorMetadataFactory = $this->prophesize(MetadataFactoryInterface::class); + $validatorMetadataFactory->getMetadataFor(DummyValidatedEntity::class)->willReturn($this->validatorClassMetadata)->shouldBeCalled(); + + $validatorPropertyMetadataFactory = new ValidatorPropertyMetadataFactory( + $validatorMetadataFactory->reveal(), + $decoratedPropertyMetadataFactory->reveal(), + [] + ); + $resultedPropertyMetadata = $validatorPropertyMetadataFactory->create(DummyValidatedEntity::class, 'dummyGroup', ['validation_groups' => $groups]); + + $this->assertEquals($expectedPropertyMetadata, $resultedPropertyMetadata); + } + public function testCreateWithPropertyWithBadValidationGroupsAndRequiredConstraints(): void { $propertyMetadata = (new ApiProperty())->withDescription('A dummy group')->withReadable(true)->withWritable(true); From 13fe8e78c394726704f71e3b30cbfffdaeae1a6f Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 15 Apr 2026 16:03:53 +0200 Subject: [PATCH 3/3] Fix cs --- .php-cs-fixer.dist.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 457c7ce6eb..4a493adc58 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -48,6 +48,7 @@ ], 'array_indentation' => true, 'compact_nullable_typehint' => true, + 'declare_strict_types' => true, 'doctrine_annotation_array_assignment' => [ 'operator' => '=', ], @@ -123,4 +124,3 @@ new SymfonyServiceClassConstantFixer(), ]) ->setFinder($finder); -