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

Skip to content
Sign in

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d5d9c03

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

20 files changed

+278
-55
lines changed

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff F438 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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 9 additions & 0 deletions
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

Lines changed: 46 additions & 43 deletions
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 10000 ()]);
23882391

23892392
if (!$compile) {

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 2 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 6 additions & 0 deletions
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
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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;
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+
}
Lines changed: 46 additions & 0 deletions
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