Skip to content

Commit 290ee61

Browse files
authored
Merge pull request #1198 from dunglas/validator_service
Allow to use a service for dynamic validation groups
2 parents 1d4690b + b325e5e commit 290ee61

File tree

3 files changed

+59
-13
lines changed

3 files changed

+59
-13
lines changed

src/Bridge/Symfony/Bundle/Resources/config/validator.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<service id="api_platform.listener.view.validate" class="ApiPlatform\Core\Bridge\Symfony\Validator\EventListener\ValidateListener">
1414
<argument type="service" id="validator" />
1515
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
16+
<argument type="service" id="service_container" />
1617

1718
<tag name="kernel.event_listener" event="kernel.view" method="onKernelView" priority="64" />
1819
</service>

src/Bridge/Symfony/Validator/EventListener/ValidateListener.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Core\Bridge\Symfony\Validator\Exception\ValidationException;
1717
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
1818
use ApiPlatform\Core\Util\RequestAttributesExtractor;
19+
use Psr\Container\ContainerInterface;
1920
use Symfony\Component\HttpFoundation\Request;
2021
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
2122
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -29,11 +30,13 @@ final class ValidateListener
2930
{
3031
private $validator;
3132
private $resourceMetadataFactory;
33+
private $container;
3234

33-
public function __construct(ValidatorInterface $validator, ResourceMetadataFactoryInterface $resourceMetadataFactory)
35+
public function __construct(ValidatorInterface $validator, ResourceMetadataFactoryInterface $resourceMetadataFactory, ContainerInterface $container = null)
3436
{
3537
$this->validator = $validator;
3638
$this->resourceMetadataFactory = $resourceMetadataFactory;
39+
$this->container = $container;
3740
}
3841

3942
/**
@@ -69,7 +72,14 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
6972
$validationGroups = $resourceMetadata->getAttributes()['validation_groups'] ?? null;
7073
}
7174

72-
if (is_callable($validationGroups)) {
75+
if (
76+
$this->container &&
77+
$this->container->has($validationGroups) &&
78+
($service = $this->container->get($validationGroups)) &&
79+
is_callable($service)
80+
) {
81+
$validationGroups = $service($data);
82+
} elseif (is_callable($validationGroups)) {
7383
$validationGroups = call_user_func_array($validationGroups, [$data]);
7484
}
7585

tests/Bridge/Symfony/Validator/EventListener/ValidateListenerTest.php

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
1818
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
1919
use ApiPlatform\Core\Tests\Fixtures\DummyEntity;
20+
use Psr\Container\ContainerInterface;
2021
use Symfony\Component\HttpFoundation\Request;
2122
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
2223
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -63,6 +64,49 @@ public function testValidatorIsCalled()
6364
$validationViewListener->onKernelView($event);
6465
}
6566

67+
public function testGetGroupsFromCallable()
68+
{
69+
$data = new DummyEntity();
70+
$expectedValidationGroups = ['a', 'b', 'c'];
71+
72+
$validatorProphecy = $this->prophesize(ValidatorInterface::class);
73+
$validatorProphecy->validate($data, null, $expectedValidationGroups)->shouldBeCalled();
74+
$validator = $validatorProphecy->reveal();
75+
76+
$closure = function ($data) use ($expectedValidationGroups): array {
77+
return $data instanceof DummyEntity ? $expectedValidationGroups : [];
78+
};
79+
80+
list($resourceMetadataFactory, $event) = $this->createEventObject($closure, $data);
81+
82+
$validationViewListener = new ValidateListener($validator, $resourceMetadataFactory);
83+
$validationViewListener->onKernelView($event);
84+
}
85+
86+
public function testGetGroupsFromService()
87+
{
88+
$data = new DummyEntity();
89+
90+
$validatorProphecy = $this->prophesize(ValidatorInterface::class);
91+
$validatorProphecy->validate($data, null, ['a', 'b', 'c'])->shouldBeCalled();
92+
$validator = $validatorProphecy->reveal();
93+
94+
list($resourceMetadataFactory, $event) = $this->createEventObject('groups_builder', $data);
95+
96+
$containerProphecy = $this->prophesize(ContainerInterface::class);
97+
$containerProphecy->has('groups_builder')->willReturn(true)->shouldBeCalled();
98+
$containerProphecy->get('groups_builder')->willReturn(new class() {
99+
public function __invoke($data): array
100+
{
101+
return $data instanceof DummyEntity ? ['a', 'b', 'c'] : [];
102+
}
103+
}
104+
)->shouldBeCalled();
105+
106+
$validationViewListener = new ValidateListener($validator, $resourceMetadataFactory, $containerProphecy->reveal());
107+
$validationViewListener->onKernelView($event);
108+
}
109+
66110
public function testDoNotCallWhenReceiveFlagIsFalse()
67111
{
68112
$data = new DummyEntity();
@@ -100,19 +144,10 @@ public function testThrowsValidationExceptionWithViolationsFound()
100144
$validationViewListener->onKernelView($event);
101145
}
102146

103-
/**
104-
* @param array $expectedValidationGroups
105-
* @param mixed $data
106-
* @param bool $receive
107-
*
108-
* @return array
109-
*/
110-
private function createEventObject($expectedValidationGroups, $data, bool $receive = true)
147+
private function createEventObject($expectedValidationGroups, $data, bool $receive = true): array
111148
{
112149
$resourceMetadata = new ResourceMetadata(null, null, null, [
113-
'create' => [
114-
'validation_groups' => $expectedValidationGroups,
115-
],
150+
'create' => ['validation_groups' => $expectedValidationGroups],
116151
]);
117152

118153
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);

0 commit comments

Comments
 (0)