8000 Allow implementing GroupSequenceProviderInterface outside the underly… · symfony/symfony@d3c23a3 · GitHub
[go: up one dir, main page]

Skip to content

Commit d3c23a3

Browse files
committed
Allow implementing GroupSequenceProviderInterface outside the underlying class
1 parent f0959b4 commit d3c23a3

20 files changed

+278
-55
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class UnusedTagsPass implements CompilerPassInterface
101101
'twig.runtime',
102102
'validator.auto_mapper',
103103
'validator.constraint_validator',
104+
'validator.group_sequence_provider',
104105
'validator.initializer',
105106
'workflow',
106107
];

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+3
Original file line numberDiff line number F438 Diff line change
@@ -184,6 +184,7 @@
184184
use Symfony\Component\Uid\UuidV4;
185185
use Symfony\Component\Validator\Constraints\WhenValidator;
186186
use Symfony\Component\Validator\ConstraintValidatorInterface;
187+
use Symfony\Component\Validator\GroupSequenceProviderInterface;
187188
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
188189
use Symfony\Component\Validator\ObjectInitializerInterface;
189190
use Symfony\Component\Validator\Validation;
@@ -669,6 +670,8 @@ public function load(array $configs, ContainerBuilder $container)
669670
->addTag('serializer.normalizer');
670671
$container->registerForAutoconfiguration(ConstraintValidatorInterface::class)
671672
->addTag('validator.constraint_validator');
673+
$container->registerForAutoconfiguration(GroupSequenceProviderInterface::class)
674+
->addTag('validator.group_sequence_provider');
672675
$container->registerForAutoconfiguration(ObjectInitializerInterface::class)
673676
->addTag('validator.initializer');
674677
$container->registerForAutoconfiguration(MessageHandlerInterface::class)

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
use Symfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
6868
use Symfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass;
6969
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
70+
use Symfony\Component\Validator\DependencyInjection\AddGroupSequenceProvidersPass;
7071
use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass;
7172
use Symfony\Component\VarExporter\Internal\Hydrator;
7273
use Symfony\Component\VarExporter\Internal\Registry;
@@ -148,6 +149,7 @@ public function build(ContainerBuilder $container)
148149
// but as late as possible to get resolved parameters
149150
$container->addCompilerPass($registerListenersPass, PassConfig::TYPE_BEFORE_REMOVING);
150151
$this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class);
152+
$this->addCompilerPassIfExists($container, AddGroupSequenceProvidersPass::class);
151153
$container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255);
152154
$this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class);
153155
$this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING);

src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php

+9
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\Validator\Constraints\NotCompromisedPasswordValidator;
2121
use Symfony\Component\Validator\Constraints\WhenValidator;
2222
use Symfony\Component\Validator\ContainerConstraintValidatorFactory;
23+
use Symfony\Component\Validator\ContainerGroupSequenceProviderFactory;
2324
use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
2425
use Symfony\Component\Validator\Validation;
2526
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -41,6 +42,9 @@
4142
->call('setConstraintValidatorFactory', [
4243
service('validator.validator_factory'),
4344
])
45+
->call('setGroupSequenceProviderFactory', [
46+
service('validator.group_sequence_provider_factory'),
47+
])
4448
->call('setTranslator', [
4549
service('translator')->ignoreOnInvalid(),
4650
])
@@ -68,6 +72,11 @@
6872
abstract_arg('Constraint validators locator'),
6973
])
7074

75+
->set('validator.group_sequence_provider_factory', ContainerGroupSequenceProviderFactory::class)
76+
->args([
77+
abstract_arg('Group sequence provider locator'),
78+
])
79+
7180
->load('Symfony\Component\Validator\Constraints\\', $validatorsDir.'/*Validator.php')
7281
->exclude($validatorsDir.'/ExpressionLanguageSyntaxValidator.php')
7382
->abstract()

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php

+46-43
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
use Symfony\Component\Translation\DependencyInjection\TranslatorPass;
8181
use Symfony\Component\Translation\LocaleSwitcher;
8282
use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
83+
use Symfony\Component\Validator\DependencyInjection\AddGroupSequenceProvidersPass;
8384
use Symfony\Component\Validator\Validation;
8485
use Symfony\Component\Validator\Validator\ValidatorInterface;
8586
use Symfony\Component\Validator\ValidatorBuilder;
@@ -1230,16 +1231,18 @@ public function testValidation()
12301231

12311232
$annotations = !class_exists(FullStack::class);
12321233

1233-
$this->assertCount($annotations ? 7 : 6, $calls);
1234+
$this->assertCount($annotations ? 8 : 7, $calls);
12341235
$this->assertSame('setConstraintValidatorFactory', $calls[0][0]);
12351236
$this->assertEquals([new Reference('validator.validator_factory')], $calls[0][1]);
1236-
$this->assertSame('setTranslator', $calls[1][0]);
1237-
$this->assertEquals([new Reference('translator', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)], $calls[1][1]);
1238-
$this->assertSame('setTranslationDomain', $calls[2][0]);
1239-
$this->assertSame(['%validator.translation_domain%'], $calls[2][1]);
1240-
$this->assertSame('addXmlMappings', $calls[3][0]);
1241-
$this->assertSame([$xmlMappings], $calls[3][1]);
1242-
$i = 3;
1237+
$this->assertSame('setGroupSequenceProviderFactory', $calls[1][0]);
1238+
$this->assertEquals([new Reference('validator.group_sequence_provider_factory')], $calls[1][1]);
1239+
$this->assertSame('setTranslator', $calls[2][0]);
1240+
$this->assertEquals([new Reference('translator', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)], $calls[2][1]);
1241+
$this->assertSame('setTranslationDomain', $calls[3][0]);
1242+
$this->assertSame(['%validator.translation_domain%'], $calls[3][1]);
1243+
$this->assertSame('addXmlMappings', $calls[4][0]);
1244+
$this->assertSame([$xmlMappings], $calls[4][1]);
1245+
$i = 4;
12431246
if ($annotations) {
12441247
$this->assertSame('enableAnnotationMapping', $calls[++$i][0]);
12451248
}
@@ -1288,12 +1291,12 @@ public function testValidationAnnotations()
12881291

12891292
$calls = $container->getDefinition('validator.builder')->getMethodCalls();
12901293

1291-
$this->assertCount(7, $calls);
1292-
$this->assertSame('enableAnnotationMapping', $calls[4][0]);
1293-
$this->assertSame('addMethodMapping', $calls[5][0]);
1294-
$this->assertSame(['loadValidatorMetadata'], $calls[5][1]);
1295-
$this->assertSame('setMappingCache', $calls[6][0]);
1296-
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[6][1]);
1294+
$this->assertCount(8, $calls);
1295+
$this->assertSame('enableAnnotationMapping', $calls[5][0]);
1296+
$this->assertSame('addMethodMapping', $calls[6][0]);
1297+
$this->assertSame(['loadValidatorMetadata'], $calls[6][1]);
1298+
$this->assertSame('setMappingCache', $calls[7][0]);
1299+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[7][1]);
12971300
// no cache this time
12981301
}
12991302

@@ -1308,14 +1311,14 @@ public function testValidationLegacyAnnotations()
13081311

13091312
$calls = $container->getDefinition('validator.builder')->getMethodCalls();
13101313

1311-
$this->assertCount(8, $calls);
1312-
$this->assertSame('enableAnnotationMapping', $calls[4][0]);
1314+
$this->assertCount(9, $calls);
1315+
$this->assertSame('enableAnnotationMapping', $calls[5][0]);
13131316
if (method_exists(ValidatorBuilder::class, 'setDoctrineAnnotationReader')) {
1314-
$this->assertSame('setDoctrineAnnotationReader', $calls[5][0]);
1315-
$this->assertEquals([new Reference('annotation_reader')], $calls[5][1]);
1316-
$i = 6;
1317+
$this->assertSame('setDoctrineAnnotationReader', $calls[6][0]);
1318+
$this->assertEquals([new Reference('annotation_reader')], $calls[6][1]);
1319+
$i = 7;
13171320
} else {
1318-
$i = 5;
1321+
$i = 6;
13191322
}
13201323
$this->assertSame('addMethodMapping', $calls[$i][0]);
13211324
$this->assertSame(['loadValidatorMetadata'], $calls[$i][1]);
@@ -1335,16 +1338,16 @@ public function testValidationPaths()
13351338

13361339
$calls = $container->getDefinition('validator.builder')->getMethodCalls();
13371340

1338-
$this->assertCount(8, $calls);
1339-
$this->assertSame('addXmlMappings', $calls[3][0]);
1340-
$this->assertSame('addYamlMappings', $calls[4][0]);
1341-
$this->assertSame('enableAnnotationMapping', $calls[5][0]);
1342-
$this->assertSame('addMethodMapping', $calls[6][0]);
1343-
$this->assertSame(['loadValidatorMetadata'], $calls[6][1]);
1344-
$this->assertSame('setMappingCache', $calls[7][0]);
1345-
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[7][1]);
1341+
$this->assertCount(9, $calls);
1342+
$this->assertSame('addXmlMappings', $calls[4][0]);
1343+
$this->assertSame('addYamlMappings', $calls[5][0]);
1344+
$this->assertSame('enableAnnotationMapping', $calls[6][0]);
1345+
$this->assertSame('addMethodMapping', $calls[7][0]);
1346+
$this->assertSame(['loadValidatorMetadata'], $calls[7][1]);
1347+
$this->assertSame('setMappingCache', $calls[8][0]);
1348+
$this->assertEquals([new Reference('validator.mapping.cache.adapter')], $calls[8][1]);
13461349

1347-
$xmlMappings = $calls[3][1][0];
1350+
$xmlMappings = $calls[4][1][0];
13481351
$this->assertCount(3, $xmlMappings);
13491352
try {
13501353
// Testing symfony/symfony
@@ -1355,7 +1358,7 @@ public function testValidationPaths()
13551358
}
13561359
$this->assertStringEndsWith('TestBundle/Resources/config/validation.xml', $xmlMappings[1]);
13571360

1358-
$yamlMappings = $calls[4][1][0];
1361+
$yamlMappings = $calls[5][1][0];
13591362
$this->assertCount(1, $yamlMappings);
13601363
$this->assertStringEndsWith('TestBundle/Resources/config/validation.yml', $yamlMappings[0]);
13611364
}
@@ -1370,7 +1373,7 @@ public function testValidationPathsUsingCustomBundlePath()
13701373
]);
13711374

13721375
$calls = $container->getDefinition('validator.builder')->getMethodCalls();
1373-
$xmlMappings = $calls[3][1][0];
1376+
$xmlMappings = $calls[4][1][0];
13741377
$this->assertCount(3, $xmlMappings);
13751378

13761379
try {
@@ -1382,7 +1385,7 @@ public function testValidationPathsUsingCustomBundlePath()
13821385
}
13831386
$this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.xml', $xmlMappings[1]);
13841387

1385-
$yamlMappings = $calls[4][1][0];
1388+
$yamlMappings = $calls[5][1][0];
13861389
$this->assertCount(1, $yamlMappings);
13871390
$this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.yml', $yamlMappings[0]);
13881391
}
@@ -1395,9 +1398,9 @@ public function testValidationNoStaticMethod()
13951398

13961399
$annotations = !class_exists(FullStack::class);
13971400

1398-
$this->assertCount($annotations ? 6 : 5, $calls);
1399-
$this->assertSame('addXmlMappings', $calls[3][0]);
1400-
$i = 3;
1401+
$this->assertCount($annotations ? 7 : 6, $calls);
1402+
$this->assertSame('addXmlMappings', $calls[4][0]);
1403+
$i = 4;
14011404
if ($annotations) {
14021405
$this->assertSame('enableAnnotationMapping', $calls[++$i][0]);
14031406
}
@@ -1426,14 +1429,14 @@ public function testValidationMapping()
14261429

14271430
$calls = $container->getDefinition('validator.builder')->getMethodCalls();
14281431

1429-
$this->assertSame('addXmlMappings', $calls[3][0]);
1430-
$this->assertCount(3, $calls[3][1][0]);
1431-
1432-
$this->assertSame('addYamlMappings', $calls[4][0]);
1432+
$this->assertSame('addXmlMappings', $calls[4][0]);
14331433
$this->assertCount(3, $calls[4][1][0]);
1434-
$this->assertStringContainsString('foo.yml', $calls[4][1][0][0]);
1435-
$this->assertStringContainsString('validation.yml', $calls[4][1][0][1]);
1436-
$this->assertStringContainsString('validation.yaml', $calls[4][1][0][2]);
1434+
1435+
$this->assertSame('addYamlMappings', $calls[5][0]);
1436+
$this->assertCount(3, $calls[5][1][0]);
1437+
$this->assertStringContainsString('foo.yml', $calls[5][1][0][0]);
1438+
$this->assertStringContainsString('validation.yml', $calls[5][1][0][1]);
1439+
$this->assertStringContainsString('validation.yaml', $calls[5][1][0][2]);
14371440
}
14381441

14391442
public function testValidationAutoMapping()
@@ -2383,7 +2386,7 @@ protected function createContainerFromFile(string $file, array $data = [], bool
23832386
$container->getCompilerPassConfig()->setAfterRemovingPasses([]);
23842387
}
23852388
$container->getCompilerPassConfig()->setBeforeOptimizationPasses([new LoggerPass()]);
2386-
$container->getCompilerPassConfig()->setBeforeRemovingPasses([new AddConstraintValidatorsPass(), new TranslatorPass()]);
2389+
$container->getCompilerPassConfig()->setBeforeRemovingPasses([new AddConstraintValidatorsPass(), new AddGroupSequenceProvidersPass(), new TranslatorPass()]);
23872390
$container->getCompilerPassConfig()->setAfterRemovingPasses([new AddAnnotationsCachedReaderPass()]);
23882391

23892392
if (!$compile) {

src/Symfony/Bundle/FrameworkBundle/composer.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"symfony/string": "^5.4|^6.0|^7.0",
6565
"symfony/translation": "^6.2.8|^7.0",
6666
"symfony/twig-bundle": "^5.4|^6.0|^7.0",
67-
"symfony/validator": "^6.3|^7.0",
67+
"symfony/validator": "^6.4|^7.0",
6868
"symfony/workflow": "^5.4|^6.0|^7.0",
6969
"symfony/yaml": "^5.4|^6.0|^7.0",
7070
"symfony/property-info": "^5.4|^6.0|^7.0",
@@ -98,7 +98,7 @@
9898
"symfony/translation": "<6.2.8",
9999
"symfony/twig-bridge": "<5.4",
100100
"symfony/twig-bundle": "<5.4",
101-
"symfony/validator": "<6.3",
101+
"symfony/validator": "<6.4",
102102
"symfony/web-profiler-bundle": "<5.4",
103103
"symfony/workflow": "<5.4"
104104
},

src/Symfony/Component/Validator/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CHANGELOG
1212
* Deprecate `ValidatorBuilder::addDefaultDoctrineAnnotationReader()`
1313
* Add `number`, `finite-number` and `finite-float` types to `Type` constraint
1414
* Add the `withSeconds` option to the `Time` constraint that allows to pass time without seconds
15+
* Add support for implementing `GroupSequenceProviderInterface` outside of the underlying class
1516

1617
6.3
1718
---

src/Symfony/Component/Validator/Constraints/GroupSequenceProvider.php

+6
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,10 @@
2323
#[\Attribute(\Attribute::TARGET_CLASS)]
2424
class GroupSequenceProvider
2525
{
26+
public ?string $class = null;
27+
28+
public function __construct(array $options = [], string $class = null)
29+
{
30+
$this->class = $options['class'] ?? $class;
31+
}
2632
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Microservices bundle.
5+
*
6+
* (c) Yonel Ceruto <yonelceruto@gmail.com>
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+
namespace Symfony\Component\Validator;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\Validator\Constraints\GroupSequenceProvider;
16+
use Symfony\Component\Validator\Exception\GroupDefinitionException;
17+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
18+
19+
/**
20+
* Factory class leveraging a PSR-11 container to create or retrieve instances of group sequence providers.
21+
*
22+
* @author Yonel Ceruto <yonelceruto@gmail.com>
23+
*/
24+
class ContainerGroupSequenceProviderFactory implements GroupSequenceProviderFactoryInterface
25+
{
26+
private array $providers = [];
27+
28+
public function __construct(private readonly ContainerInterface $container)
29+
{
30+
}
31+
32+
public function getInstance(string $class): GroupSequenceProviderInterface
33+
{
34+
if (!isset($this->providers[$class])) {
35+
if ($this->container->has($class)) {
36+
$this->providers[$class] = $this->container->get($class);
37+
} else {
38+
if (!class_exists($class)) {
39+
throw new GroupDefinitionException(sprintf('Group sequence provider "%s" does not exist or is not enabled as service. Check the "class" option in your constraint class "%s".', $class, GroupSequenceProvider::class));
40+
}
41+
42+
$this->providers[$class] = new $class();
43+
}
44+
}
45+
46+
if (!$this->providers[$class] instanceof GroupSequenceProviderInterface) {
47+
throw new UnexpectedTypeException($this->providers[$class], GroupSequenceProviderInterface::class);
48+
}
49+
50+
return $this->providers[$class];
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\Validator\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
19+
/**
20+
* @author Yonel Ceruto <yonelceruto@gmail.com>
21+
*/
22+
class AddGroupSequenceProvidersPass implements CompilerPassInterface
23+
{
24+
public function process(ContainerBuilder $container): void
25+
{
26+
if (!$container->hasDefinition('validator.group_sequence_provider_factory')) {
27+
return;
28+
}
29+
30+
$providers = [];
31+
foreach ($container->findTaggedServiceIds('validator.group_sequence_provider', true) as $id => $attributes) {
32+
$definition = $container->getDefinition($id);
33+
34+
if (isset($attributes[0]['alias'])) {
35+
$providers[$attributes[0]['alias']] = new Reference($id);
36+
}
37+
38+
$providers[$definition->getClass()] = new Reference($id);
39+
}
40+
41+
$container
42+
->getDefinition('validator.group_sequence_provider_factory')
43+
->replaceArgument(0, ServiceLocatorTagPass::register($container, $providers))
44+
;
45+
}
46+
}

0 commit comments

Comments
 (0)
0