Skip to content

Commit 9bfa5fe

Browse files
c1tru55soyuka
andauthored
fix(metadata): call dynamic validation groups #7184 (#7184)
Co-authored-by: soyuka <[email protected]>
1 parent 296bad4 commit 9bfa5fe

File tree

3 files changed

+70
-28
lines changed

3 files changed

+70
-28
lines changed

src/Symfony/Validator/Metadata/Property/ValidatorPropertyMetadataFactory.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use ApiPlatform\Metadata\ApiProperty;
1818
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
1919
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface;
20+
use ApiPlatform\Symfony\Validator\ValidationGroupsExtractorTrait;
21+
use Psr\Container\ContainerInterface;
2022
use Symfony\Component\Validator\Constraint;
2123
use Symfony\Component\Validator\Constraints\Bic;
2224
use Symfony\Component\Validator\Constraints\CardScheme;
@@ -47,6 +49,10 @@
4749
*/
4850
final class ValidatorPropertyMetadataFactory implements PropertyMetadataFactoryInterface
4951
{
52+
use ValidationGroupsExtractorTrait {
53+
getValidationGroups as extractValidationGroups;
54+
}
55+
5056
/**
5157
* @var string[] A list of constraint classes making the entity required
5258
*/
@@ -72,8 +78,13 @@ final class ValidatorPropertyMetadataFactory implements PropertyMetadataFactoryI
7278
/**
7379
* @param PropertySchemaRestrictionMetadataInterface[] $restrictionsMetadata
7480
*/
75-
public function __construct(private readonly ValidatorMetadataFactoryInterface $validatorMetadataFactory, private readonly PropertyMetadataFactoryInterface $decorated, private readonly iterable $restrictionsMetadata = [])
76-
{
81+
public function __construct(
82+
private readonly ValidatorMetadataFactoryInterface $validatorMetadataFactory,
83+
private readonly PropertyMetadataFactoryInterface $decorated,
84+
private readonly iterable $restrictionsMetadata = [],
85+
?ContainerInterface $container = null,
86+
) {
87+
$this->container = $container;
7788
}
7889

7990
/**
@@ -151,11 +162,8 @@ public function create(string $resourceClass, string $property, array $options =
151162
*/
152163
private function getValidationGroups(ValidatorClassMetadataInterface $classMetadata, array $options): array
153164
{
154-
if (
155-
isset($options['validation_groups'])
156-
&& !\is_callable($options['validation_groups'])
157-
) {
158-
return $options['validation_groups'];
165+
if (null !== ($groups = $this->extractValidationGroups($options['validation_groups'] ?? null))) {
166+
return $groups;
159167
}
160168

161169
if (!method_exists($classMetadata, 'getDefaultGroup')) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Symfony\Validator;
15+
16+
use Psr\Container\ContainerInterface;
17+
use Symfony\Component\Validator\Constraints\GroupSequence;
18+
19+
trait ValidationGroupsExtractorTrait
20+
{
21+
/**
22+
* A service locator for ValidationGroupsGenerator.
23+
*/
24+
private ?ContainerInterface $container = null;
25+
26+
public function getValidationGroups(\Closure|array|string|null $validationGroups, ?object $data = null): string|array|GroupSequence|null
27+
{
28+
if (null === $validationGroups) {
29+
return $validationGroups;
30+
}
31+
32+
if (
33+
$this->container
34+
&& \is_string($validationGroups)
35+
&& $this->container->has($validationGroups)
36+
&& ($service = $this->container->get($validationGroups))
37+
&& \is_callable($service)
38+
) {
39+
$validationGroups = $service($data);
40+
} elseif (\is_callable($validationGroups)) {
41+
$validationGroups = $validationGroups($data);
42+
}
43+
44+
if (!$validationGroups instanceof GroupSequence) {
45+
$validationGroups = (array) $validationGroups;
46+
}
47+
48+
return $validationGroups;
49+
}
50+
}

src/Symfony/Validator/Validator.php

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use ApiPlatform\Validator\Exception\ValidationException;
1717
use ApiPlatform\Validator\ValidatorInterface;
1818
use Psr\Container\ContainerInterface;
19-
use Symfony\Component\Validator\Constraints\GroupSequence;
2019
use Symfony\Component\Validator\Validator\ValidatorInterface as SymfonyValidatorInterface;
2120

2221
/**
@@ -26,34 +25,19 @@
2625
*/
2726
final class Validator implements ValidatorInterface
2827
{
29-
public function __construct(private readonly SymfonyValidatorInterface $validator, private readonly ?ContainerInterface $container = null)
28+
use ValidationGroupsExtractorTrait;
29+
30+
public function __construct(private readonly SymfonyValidatorInterface $validator, ?ContainerInterface $container = null)
3031
{
32+
$this->container = $container;
3133
}
3234

3335
/**
3436
* {@inheritdoc}
3537
*/
3638
public function validate(object $data, array $context = []): void
3739
{
38-
if (null !== $validationGroups = $context['groups'] ?? null) {
39-
if (
40-
$this->container
41-
&& \is_string($validationGroups)
42-
&& $this->container->has($validationGroups)
43-
&& ($service = $this->container->get($validationGroups))
44-
&& \is_callable($service)
45-
) {
46-
$validationGroups = $service($data);
47-
} elseif (\is_callable($validationGroups)) {
48-
$validationGroups = $validationGroups($data);
49-
}
50-
51-
if (!$validationGroups instanceof GroupSequence) {
52-
$validationGroups = (array) $validationGroups;
53-
}
54-
}
55-
56-
$violations = $this->validator->validate($data, null, $validationGroups);
40+
$violations = $this->validator->validate($data, null, $this->getValidationGroups($context['groups'] ?? null, $data));
5741
if (0 !== \count($violations)) {
5842
throw new ValidationException($violations);
5943
}

0 commit comments

Comments
 (0)