diff --git a/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php b/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php index 5aba1bb66da..f2cb40b0ff5 100644 --- a/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php +++ b/src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php @@ -17,8 +17,6 @@ use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface; use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface; -use ApiPlatform\Symfony\Validator\ValidationGroupsExtractorTrait; -use Psr\Container\ContainerInterface; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Bic; use Symfony\Component\Validator\Constraints\CardScheme; @@ -28,6 +26,7 @@ use Symfony\Component\Validator\Constraints\DateTime; use Symfony\Component\Validator\Constraints\Email; use Symfony\Component\Validator\Constraints\File; +use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\Iban; use Symfony\Component\Validator\Constraints\Image; use Symfony\Component\Validator\Constraints\Isbn; @@ -49,10 +48,6 @@ */ final class ValidatorPropertyMetadataFactory implements PropertyMetadataFactoryInterface { - use ValidationGroupsExtractorTrait { - getValidationGroups as extractValidationGroups; - } - /** * @var string[] A list of constraint classes making the entity required */ @@ -78,13 +73,8 @@ final class ValidatorPropertyMetadataFactory implements PropertyMetadataFactoryI /** * @param PropertySchemaRestrictionMetadataInterface[] $restrictionsMetadata */ - public function __construct( - private readonly ValidatorMetadataFactoryInterface $validatorMetadataFactory, - private readonly PropertyMetadataFactoryInterface $decorated, - private readonly iterable $restrictionsMetadata = [], - ?ContainerInterface $container = null, - ) { - $this->container = $container; + public function __construct(private readonly ValidatorMetadataFactoryInterface $validatorMetadataFactory, private readonly PropertyMetadataFactoryInterface $decorated, private readonly iterable $restrictionsMetadata = []) + { } /** @@ -162,8 +152,14 @@ public function create(string $resourceClass, string $property, array $options = */ private function getValidationGroups(ValidatorClassMetadataInterface $classMetadata, array $options): array { - if (null !== ($groups = $this->extractValidationGroups($options['validation_groups'] ?? null))) { - return $groups; + if (isset($options['validation_groups'])) { + if ($options['validation_groups'] instanceof GroupSequence) { + return $options['validation_groups']->groups; + } + + if (!\is_callable($options['validation_groups'])) { + return $options['validation_groups']; + } } if (!method_exists($classMetadata, 'getDefaultGroup')) { diff --git a/src/Symfony/Validator/ValidationGroupsExtractorTrait.php b/src/Symfony/Validator/ValidationGroupsExtractorTrait.php deleted file mode 100644 index 36f44e9a915..00000000000 --- a/src/Symfony/Validator/ValidationGroupsExtractorTrait.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace ApiPlatform\Symfony\Validator; - -use Psr\Container\ContainerInterface; -use Symfony\Component\Validator\Constraints\GroupSequence; - -trait ValidationGroupsExtractorTrait -{ - /** - * A service locator for ValidationGroupsGenerator. - */ - private ?ContainerInterface $container = null; - - public function getValidationGroups(\Closure|array|GroupSequence|string|null $validationGroups, ?object $data = null): string|array|GroupSequence|null - { - if (null === $validationGroups) { - return $validationGroups; - } - - if ( - $this->container - && \is_string($validationGroups) - && $this->container->has($validationGroups) - && ($service = $this->container->get($validationGroups)) - && \is_callable($service) - ) { - $validationGroups = $service($data); - } elseif (\is_callable($validationGroups)) { - $validationGroups = $validationGroups($data); - } - - if (!$validationGroups instanceof GroupSequence) { - $validationGroups = (array) $validationGroups; - } - - return $validationGroups; - } -} diff --git a/src/Symfony/Validator/Validator.php b/src/Symfony/Validator/Validator.php index 0b21eebe3b5..a0645ab9968 100644 --- a/src/Symfony/Validator/Validator.php +++ b/src/Symfony/Validator/Validator.php @@ -16,6 +16,7 @@ use ApiPlatform\Validator\Exception\ValidationException; use ApiPlatform\Validator\ValidatorInterface; use Psr\Container\ContainerInterface; +use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Validator\ValidatorInterface as SymfonyValidatorInterface; /** @@ -25,11 +26,8 @@ */ final class Validator implements ValidatorInterface { - use ValidationGroupsExtractorTrait; - - public function __construct(private readonly SymfonyValidatorInterface $validator, ?ContainerInterface $container = null) + public function __construct(private readonly SymfonyValidatorInterface $validator, private readonly ?ContainerInterface $container = null) { - $this->container = $container; } /** @@ -37,7 +35,25 @@ public function __construct(private readonly SymfonyValidatorInterface $validato */ public function validate(object $data, array $context = []): void { - $violations = $this->validator->validate($data, null, $this->getValidationGroups($context['groups'] ?? null, $data)); + if (null !== $validationGroups = $context['groups'] ?? null) { + if ( + $this->container + && \is_string($validationGroups) + && $this->container->has($validationGroups) + && ($service = $this->container->get($validationGroups)) + && \is_callable($service) + ) { + $validationGroups = $service($data); + } elseif (\is_callable($validationGroups)) { + $validationGroups = $validationGroups($data); + } + + if (!$validationGroups instanceof GroupSequence) { + $validationGroups = (array) $validationGroups; + } + } + + $violations = $this->validator->validate($data, null, $validationGroups); if (0 !== \count($violations)) { throw new ValidationException($violations); } diff --git a/tests/Symfony/Validator/ValidatorTest.php b/tests/Symfony/Validator/ValidatorTest.php index c678469a66e..161e6a6280b 100644 --- a/tests/Symfony/Validator/ValidatorTest.php +++ b/tests/Symfony/Validator/ValidatorTest.php @@ -20,6 +20,7 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Psr\Container\ContainerInterface; +use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationListInterface; @@ -119,4 +120,22 @@ public function testValidatorWithScalarGroup(): void $validator = new Validator($symfonyValidator, $containerProphecy->reveal()); $validator->validate(new DummyEntity(), ['groups' => 'foo']); } + + public function testValidatorWithGroupSequence(): void + { + $data = new DummyEntity(); + $expectedValidationGroups = new GroupSequence(['foo', 'bar']); + + $constraintViolationListProphecy = $this->prophesize(ConstraintViolationListInterface::class); + $constraintViolationListProphecy->count()->willReturn(0); + + $symfonyValidatorProphecy = $this->prophesize(SymfonyValidatorInterface::class); + $symfonyValidatorProphecy->validate($data, null, $expectedValidationGroups)->willreturn($constraintViolationListProphecy->reveal())->shouldBeCalled(); + $symfonyValidator = $symfonyValidatorProphecy->reveal(); + + $containerProphecy = $this->prophesize(ContainerInterface::class); + + $validator = new Validator($symfonyValidator, $containerProphecy->reveal()); + $validator->validate(new DummyEntity(), ['groups' => $expectedValidationGroups]); + } }