Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

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

Sign up
Appearance settings

Commit2d64e70

Browse files
committed
[Validator][DoctrineBridge][FWBundle] Automatic data validation
1 parentaf28965 commit2d64e70

File tree

19 files changed

+937
-3
lines changed

19 files changed

+937
-3
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
namespaceSymfony\Bridge\Doctrine\Tests\Fixtures;
13+
14+
useDoctrine\ORM\MappingasORM;
15+
useSymfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
16+
useSymfony\Component\Validator\ConstraintsasAssert;
17+
18+
/**
19+
* @ORM\Entity
20+
* @UniqueEntity(fields={"alreadyMappedUnique"})
21+
*
22+
* @author Kévin Dunglas <dunglas@gmail.com>
23+
*/
24+
class DoctrineLoaderEntity
25+
{
26+
/**
27+
* @ORM\Id
28+
* @ORM\Column
29+
*/
30+
public$id;
31+
32+
/**
33+
* @ORM\Column(length=20)
34+
*/
35+
public$maxLength;
36+
37+
/**
38+
* @ORM\Column(length=20)
39+
* @Assert\Length(min=5)
40+
*/
41+
public$mergedMaxLength;
42+
43+
/**
44+
* @ORM\Column(length=20)
45+
* @Assert\Length(min=1, max=10)
46+
*/
47+
public$alreadyMappedMaxLength;
48+
49+
/**
50+
* @ORM\Column(unique=true)
51+
*/
52+
public$unique;
53+
54+
/**
55+
* @ORM\Column(unique=true)
56+
*/
57+
public$alreadyMappedUnique;
58+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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+
namespaceSymfony\Bridge\Doctrine\Tests\Validator;
13+
14+
usePHPUnit\Framework\TestCase;
15+
useSymfony\Bridge\Doctrine\Test\DoctrineTestHelper;
16+
useSymfony\Bridge\Doctrine\Tests\Fixtures\DoctrineLoaderEntity;
17+
useSymfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
18+
useSymfony\Bridge\Doctrine\Validator\DoctrineLoader;
19+
useSymfony\Component\Validator\Constraints\Length;
20+
useSymfony\Component\Validator\Mapping\ClassMetadata;
21+
useSymfony\Component\Validator\Tests\Fixtures\Entity;
22+
useSymfony\Component\Validator\Validation;
23+
useSymfony\Component\Validator\ValidatorBuilder;
24+
25+
/**
26+
* @author Kévin Dunglas <dunglas@gmail.com>
27+
*/
28+
class DoctrineLoaderTestextends TestCase
29+
{
30+
publicfunctiontestLoadClassMetadata()
31+
{
32+
if (!method_exists(ValidatorBuilder::class,'addLoader')) {
33+
$this->markTestSkipped('Auto-mapping requires symfony/validation 4.2+');
34+
}
35+
36+
$validator = Validation::createValidatorBuilder()
37+
->enableAnnotationMapping()
38+
->addLoader(newDoctrineLoader(DoctrineTestHelper::createTestEntityManager()))
39+
->getValidator()
40+
;
41+
42+
$classMetadata =$validator->getMetadataFor(newDoctrineLoaderEntity());
43+
44+
$classConstraints =$classMetadata->getConstraints();
45+
$this->assertCount(2,$classConstraints);
46+
$this->assertInstanceOf(UniqueEntity::class,$classConstraints[0]);
47+
$this->assertInstanceOf(UniqueEntity::class,$classConstraints[1]);
48+
$this->assertSame(['alreadyMappedUnique'],$classConstraints[0]->fields);
49+
$this->assertSame('unique',$classConstraints[1]->fields);
50+
51+
$maxLengthMetadata =$classMetadata->getPropertyMetadata('maxLength');
52+
$this->assertCount(1,$maxLengthMetadata);
53+
$maxLengthConstraints =$maxLengthMetadata[0]->getConstraints();
54+
$this->assertCount(1,$maxLengthConstraints);
55+
$this->assertInstanceOf(Length::class,$maxLengthConstraints[0]);
56+
$this->assertSame(20,$maxLengthConstraints[0]->max);
57+
58+
$mergedMaxLengthMetadata =$classMetadata->getPropertyMetadata('mergedMaxLength');
59+
$this->assertCount(1,$mergedMaxLengthMetadata);
60+
$mergedMaxLengthConstraints =$mergedMaxLengthMetadata[0]->getConstraints();
61+
$this->assertCount(1,$mergedMaxLengthConstraints);
62+
$this->assertInstanceOf(Length::class,$mergedMaxLengthConstraints[0]);
63+
$this->assertSame(20,$mergedMaxLengthConstraints[0]->max);
64+
$this->assertSame(5,$mergedMaxLengthConstraints[0]->min);
65+
66+
$alreadyMappedMaxLengthMetadata =$classMetadata->getPropertyMetadata('alreadyMappedMaxLength');
67+
$this->assertCount(1,$alreadyMappedMaxLengthMetadata);
68+
$alreadyMappedMaxLengthConstraints =$alreadyMappedMaxLengthMetadata[0]->getConstraints();
69+
$this->assertCount(1,$alreadyMappedMaxLengthConstraints);
70+
$this->assertInstanceOf(Length::class,$alreadyMappedMaxLengthConstraints[0]);
71+
$this->assertSame(10,$alreadyMappedMaxLengthConstraints[0]->max);
72+
$this->assertSame(1,$alreadyMappedMaxLengthConstraints[0]->min);
73+
}
74+
75+
/**
76+
* @dataProvider regexpProvider
77+
*/
78+
publicfunctiontestClassValidator(bool$expected,string$classValidatorRegexp =null)
79+
{
80+
$doctrineLoader =newDoctrineLoader(DoctrineTestHelper::createTestEntityManager(),$classValidatorRegexp);
81+
82+
$classMetadata =newClassMetadata(DoctrineLoaderEntity::class);
83+
$this->assertSame($expected,$doctrineLoader->loadClassMetadata($classMetadata));
84+
}
85+
86+
publicfunctionregexpProvider()
87+
{
88+
return [
89+
[true,null],
90+
[true,'{^'.preg_quote(DoctrineLoaderEntity::class).'$|^'.preg_quote(Entity::class).'$}'],
91+
[false,'{^'.preg_quote(Entity::class).'$}'],
92+
];
93+
}
94+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
namespaceSymfony\Bridge\Doctrine\Validator;
13+
14+
useDoctrine\Common\Persistence\Mapping\MappingException;
15+
useDoctrine\ORM\EntityManagerInterface;
16+
useDoctrine\ORM\Mapping\ClassMetadataInfo;
17+
useDoctrine\ORM\Mapping\MappingExceptionasOrmMappingException;
18+
useSymfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
19+
useSymfony\Component\Validator\Constraints\Length;
20+
useSymfony\Component\Validator\Mapping\ClassMetadata;
21+
useSymfony\Component\Validator\Mapping\Loader\LoaderInterface;
22+
23+
/**
24+
* Guesses and loads the appropriate constraints using Doctrine's metadata.
25+
*
26+
* @author Kévin Dunglas <dunglas@gmail.com>
27+
*/
28+
finalclass DoctrineLoaderimplements LoaderInterface
29+
{
30+
private$entityManager;
31+
private$classValidatorRegexp;
32+
33+
publicfunction__construct(EntityManagerInterface$entityManager,string$classValidatorRegexp =null)
34+
{
35+
$this->entityManager =$entityManager;
36+
$this->classValidatorRegexp =$classValidatorRegexp;
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
publicfunctionloadClassMetadata(ClassMetadata$metadata):bool
43+
{
44+
$className =$metadata->getClassName();
45+
if (null !==$this->classValidatorRegexp && !preg_match($this->classValidatorRegexp,$className)) {
46+
returnfalse;
47+
}
48+
49+
try {
50+
$doctrineMetadata =$this->entityManager->getClassMetadata($className);
51+
}catch (MappingException |OrmMappingException$exception) {
52+
returnfalse;
53+
}
54+
55+
if (!$doctrineMetadatainstanceof ClassMetadataInfo) {
56+
returnfalse;
57+
}
58+
59+
/* Available keys:
60+
- type
61+
- scale
62+
- length
63+
- unique
64+
- nullable
65+
- precision
66+
*/
67+
$existingUniqueFields =$this->getExistingUniqueFields($metadata);
68+
69+
// Type and nullable aren't handled here, use the PropertyInfo Loader instead.
70+
foreach ($doctrineMetadata->fieldMappingsas$mapping) {
71+
if (true ===$mapping['unique'] && !isset($existingUniqueFields[$mapping['fieldName']])) {
72+
$metadata->addConstraint(newUniqueEntity(['fields' =>$mapping['fieldName']]));
73+
}
74+
75+
if (null ===$mapping['length']) {
76+
continue;
77+
}
78+
79+
$constraint =$this->getLengthConstraint($metadata,$mapping['fieldName']);
80+
if (null ===$constraint) {
81+
$metadata->addPropertyConstraint($mapping['fieldName'],newLength(['max' =>$mapping['length']]));
82+
}elseif (null ===$constraint->max) {
83+
// If a Length constraint exists and no max length has been explicitly defined, set it
84+
$constraint->max =$mapping['length'];
85+
}
86+
}
87+
88+
returntrue;
89+
}
90+
91+
privatefunctiongetLengthConstraint(ClassMetadata$metadata,string$fieldName): ?Length
92+
{
93+
foreach ($metadata->getPropertyMetadata($fieldName)as$propertyMetadata) {
94+
foreach ($propertyMetadata->getConstraints()as$constraint) {
95+
if ($constraintinstanceof Length) {
96+
return$constraint;
97+
}
98+
}
99+
}
100+
101+
returnnull;
102+
}
103+
104+
privatefunctiongetExistingUniqueFields(ClassMetadata$metadata):array
105+
{
106+
$fields = [];
107+
foreach ($metadata->getConstraints()as$constraint) {
108+
if (!$constraintinstanceof UniqueEntity) {
109+
continue;
110+
}
111+
112+
if (\is_string($constraint->fields)) {
113+
$fields[$constraint->fields] =true;
114+
}elseif (\is_array($constraint->fields) &&1 ===\count($constraint->fields)) {
115+
$fields[$constraint->fields[0]] =true;
116+
}
117+
}
118+
119+
return$fields;
120+
}
121+
}

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,45 @@ private function addValidationSection(ArrayNodeDefinition $rootNode)
792792
->end()
793793
->end()
794794
->end()
795+
->arrayNode('auto_mapping')
796+
->useAttributeAsKey('namespace')
797+
->normalizeKeys(false)
798+
->beforeNormalization()
799+
->ifArray()
800+
->then(function (array$values):array {
801+
foreach ($valuesas$k =>$v) {
802+
if (isset($v['service'])) {
803+
continue;
804+
}
805+
806+
if (isset($v['namespace'])) {
807+
$values[$k]['services'] = [];
808+
continue;
809+
}
810+
811+
if (!\is_array($v)) {
812+
$values[$v]['services'] = [];
813+
unset($values[$k]);
814+
continue;
815+
}
816+
817+
$tmp =$v;
818+
unset($values[$k]);
819+
$values[$k]['services'] =$tmp;
820+
}
821+
822+
return$values;
823+
})
824+
->end()
825+
->arrayPrototype()
826+
->fixXmlConfig('service')
827+
->children()
828+
->arrayNode('services')
829+
->prototype('scalar')->end()
830+
->end()
831+
->end()
832+
->end()
833+
->end()
795834
->end()
796835
->end()
797836
->end()

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
useSymfony\Component\Translation\Command\XliffLintCommandasBaseXliffLintCommand;
108108
useSymfony\Component\Translation\Translator;
109109
useSymfony\Component\Validator\ConstraintValidatorInterface;
110+
useSymfony\Component\Validator\Mapping\Loader\PropertyInfoLoader;
110111
useSymfony\Component\Validator\ObjectInitializerInterface;
111112
useSymfony\Component\WebLink\HttpHeaderSerializer;
112113
useSymfony\Component\Workflow;
@@ -280,7 +281,8 @@ public function load(array $configs, ContainerBuilder $container)
280281
$container->removeDefinition('console.command.messenger_debug');
281282
}
282283

283-
$this->registerValidationConfiguration($config['validation'],$container,$loader);
284+
$propertyInfoEnabled =$this->isConfigEnabled($container,$config['property_info']);
285+
$this->registerValidationConfiguration($config['validation'],$container,$loader,$propertyInfoEnabled);
284286
$this->registerEsiConfiguration($config['esi'],$container,$loader);
285287
$this->registerSsiConfiguration($config['ssi'],$container,$loader);
286288
$this->registerFragmentsConfiguration($config['fragments'],$container,$loader);
@@ -301,7 +303,7 @@ public function load(array $configs, ContainerBuilder $container)
301303
$this->registerSerializerConfiguration($config['serializer'],$container,$loader);
302304
}
303305

304-
if ($this->isConfigEnabled($container,$config['property_info'])) {
306+
if ($propertyInfoEnabled) {
305307
$this->registerPropertyInfoConfiguration($container,$loader);
306308
}
307309

@@ -1152,7 +1154,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
11521154
}
11531155
}
11541156

1155-
privatefunctionregisterValidationConfiguration(array$config,ContainerBuilder$container,XmlFileLoader$loader)
1157+
privatefunctionregisterValidationConfiguration(array$config,ContainerBuilder$container,XmlFileLoader$loader,bool$propertyInfoEnabled)
11561158
{
11571159
if (!$this->validatorConfigEnabled =$this->isConfigEnabled($container,$config)) {
11581160
return;
@@ -1203,6 +1205,11 @@ private function registerValidationConfiguration(array $config, ContainerBuilder
12031205
if (!$container->getParameter('kernel.debug')) {
12041206
$validatorBuilder->addMethodCall('setMetadataCache', [newReference('validator.mapping.cache.symfony')]);
12051207
}
1208+
1209+
$container->setParameter('validator.auto_mapping',$config['auto_mapping']);
1210+
if (!$propertyInfoEnabled || !$config['auto_mapping'] || !class_exists(PropertyInfoLoader::class)) {
1211+
$container->removeDefinition('validator.property_info_loader');
1212+
}
12061213
}
12071214

12081215
privatefunctionregisterValidatorMapping(ContainerBuilder$container,array$config,array &$files)

‎src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
useSymfony\Component\Translation\DependencyInjection\TranslationExtractorPass;
5454
useSymfony\Component\Translation\DependencyInjection\TranslatorPass;
5555
useSymfony\Component\Translation\DependencyInjection\TranslatorPathsPass;
56+
useSymfony\Component\Validator\DependencyInjection\AddAutoMappingConfigurationPass;
5657
useSymfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass;
5758
useSymfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass;
5859

@@ -124,6 +125,7 @@ public function build(ContainerBuilder $container)
124125
$container->addCompilerPass(newTestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
125126
$this->addCompilerPassIfExists($container, AddMimeTypeGuesserPass::class);
126127
$this->addCompilerPassIfExists($container, MessengerPass::class);
128+
$this->addCompilerPassIfExists($container, AddAutoMappingConfigurationPass::class);
127129
$container->addCompilerPass(newRegisterReverseContainerPass(true));
128130
$container->addCompilerPass(newRegisterReverseContainerPass(false), PassConfig::TYPE_AFTER_REMOVING);
129131

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp