8000 merged branch canni/enable_validation_groups_callback (PR #2498) · symfony/symfony@4f3e7bb · GitHub
[go: up one dir, main page]

Skip to content
10000

Commit 4f3e7bb

Browse files
committed
merged branch canni/enable_validation_groups_callback (PR #2498)
Commits ------- d08ec5e Add DelegatingValidator tests e1822e7 Enable dynamic set of validation groups by a callback or Closure Discussion ---------- [Form][Validator] Enable dynamic set of validation groups based on callback Bug fix: no Feature addition: yes Backwards compatibility break: no Symfony2 tests pass: yes Symfony2 tests written for new feature: yes closes tickets: #2498 #1151 This will allow developer to pass a Closure or a callback array as a Validation groups option of a form. Eg: ``` class ClientType extends AbstarctType { // ... public function getDefaultOptions(array $options) { return array( 'validation_groups' => function(FormInterface $form){ // return array of validation groups based on submitted user data (data is after transform) $data = $form->getData(); if($data->getType() == Entity\Client::TYPE_PERSON) return array('Default', 'person'); else return array('Default', 'company'); }, ); } // ... } ``` ``` class ClientType extends AbstarctType { // ... public function getDefaultOptions(array $options) { return array( 'validation_groups' => array( 'Acme\\AcmeBundle\\Entity\\Client', 'determineValidationGroups' ), ); } // ... } ``` This will make developers life easier ! --------------------------------------------------------------------------- by schmittjoh at 2011/10/27 06:39:56 -0700 Does that work if your ClientType were added to another form type? e.g. ```php <?php class MyComplexType extends AbstractType { public function buildForm(FormBuilder $builder, array $options) { $builder->add('client', new ClientType()); } // ... } ``` --------------------------------------------------------------------------- by canni at 2011/10/27 06:44:33 -0700 This is doing nothing more than injecting array of validation groups, should work, but I have not tested this use case. --------------------------------------------------------------------------- by canni at 2011/10/28 01:58:26 -0700 PHPUnit output ``` OK, but incomplete or skipped tests! Tests: 5011, Assertions: 12356, Incomplete: 36, Skipped: 32. ``` --------------------------------------------------------------------------- by canni at 2011/11/02 11:37:47 -0700 Now functionality is complete, test are written, and implementation is clean. :) --------------------------------------------------------------------------- by stloyd at 2011/11/02 11:50:44 -0700 Can tou `squash` your commits ? Thanks. --------------------------------------------------------------------------- by canni at 2011/11/02 11:58:41 -0700 Done --------------------------------------------------------------------------- by fabpot at 2011/11/07 07:51:18 -0800 Can you add some tests for the `DelegatingValidator` class, which is where we can ensure that the new feature actually works as expected? --------------------------------------------------------------------------- by canni at 2011/11/07 13:53:16 -0800 OK, I've written proof-of-concept tests, also I've squashed few commits to make things clear. Personally I think this should go straight into 2.0 series, as it do not beak BC, and a feature is really nice to use. --------------------------------------------------------------------------- by stof at 2011/11/07 14:17:15 -0800 @canni the 2.0 branch is for bug fixes, not for new features. This is the difference between maintenance releases and minor releases.
2 parents 8871816 + d08ec5e commit 4f3e7bb

File tree

4 files changed

+148
-3
lines changed

4 files changed

+148
-3
lines changed

src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@ public function __construct(ValidatorInterface $validator)
2727

2828
public function buildForm(FormBuilder $builder, array $options)
2929
{
30-
$options['validation_groups'] = empty($options['validation_groups'])
31-
? null
32-
: (array)$options['validation_groups'];
30+
if (empty($options['validation_groups'])) {
31+
$options['validation_groups'] = null;
32+
} else {
33+
$options['validation_groups'] = is_callable($options['validation_groups'])
34+
? $options['validation_groups']
35+
: (array) $options['validation_groups'];
36+
}
3337

3438
$builder
3539
->setAttribute('validation_groups', $options['validation_groups'])

src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ static protected function getFormValidationGroups(FormInterface $form)
133133

134134
if ($form->hasAttribute('validation_groups')) {
135135
$groups = $form->getAttribute('validation_groups');
136+
137+
if (is_callable($groups)) {
138+
$groups = (array) call_user_func($groups, $form);
139+
}
136140
}
137141

138142
$currentForm = $form;
@@ -141,6 +145,10 @@ static protected function getFormValidationGroups(FormInterface $form)
141145

142146
if ($currentForm->hasAttribute('validation_groups')) {
143147
$groups = $currentForm->getAttribute('validation_groups');
148+
149+
if (is_callable($groups)) {
150+
$groups = (array) call_user_func($groups, $currentForm);
151+
}
144152
}
145153
}
146154

tests/Symfony/Tests/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,24 @@ public function testValidationGroupsCanBeSetToArray()
3838
$this->assertEquals(array('group1', 'group2'), $form->getAttribute('validation_groups'));
3939
}
4040

41+
public function testValidationGroupsCanBeSetToCallback()
42+
{
43+
$form = $this->factory->create('field', null, array(
44+
'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'),
45+
));
46+
47+
$this->assertTrue(is_callable($form->getAttribute('validation_groups')));
48+
}
49+
50+
public function testValidationGroupsCanBeSetToClosure()
51+
{
52+
$form = $this->factory->create('field', null, array(
53+
'validation_groups' => function($data, $extraData){ return null; },
54+
));
55+
56+
$this->assertTrue(is_callable($form->getAttribute('validation_groups')));
57+
}
58+
4159
public function testBindValidatesData()
4260
{
4361
$builder = $this->factory->createBuilder('field', null, array(

tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Tests\Component\Form\Extension\Validator\Validator;
1313

14+
use Symfony\Component\Form\FormInterface;
1415
use Symfony\Component\Form\FormBuilder;
1516
use Symfony\Component\Form\FormError;
1617
use Symfony\Component\Form\Util\PropertyPath;
@@ -90,6 +91,16 @@ protected function getMockForm()
9091
return $this->getMock('Symfony\Tests\Component\Form\FormInterface');
9192
}
9293

94+
/**
95+
* Access has to be public, as this method is called via callback array
96+
* in {@link testValidateFormDataCanHandleCallbackValidationGroups()}
97+
* and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()}
98+
*/
99+
public function getValidationGroups(FormInterface $form)
100+
{
101+
return array('group1', 'group2');
102+
}
103+
93104
public function testUseValidateValueWhenValidationConstraintExist()
94105
{
95106
$constraint = $this->getMockForAbstractClass('Symfony\Component\Validator\Constraint');
@@ -597,6 +608,52 @@ public function testValidateFormData()
597608
DelegatingValidator::validateFormData($form, $context);
598609
}
599610

611+
public function testValidateFormDataCanHandleCallbackValidationGroups()
612+
{
613+
$graphWalker = $this->getMockGraphWalker();
614+
$metadataFactory = $this->getMockMetadataFactory();
615+
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
616+
$object = $this->getMock('\stdClass');
617+
$form = $this->getBuilder()
618+
->setAttribute('validation_groups', array($this, 'getValidationGroups'))
619+
->getForm();
620+
621+
$graphWalker->expects($this->at(0))
622+
->method('walkReference')
623+
->with($object, 'group1', 'data', true);
624+
$graphWalker->expects($this->at(1))
625+
->method('walkReference')
626+
->with($object, 'group2', 'data', true);
627+
628+
$form->setData($object);
629+
630+
DelegatingValidator::validateFormData($form, $context);
631+
}
632+
633+
public function testValidateFormDataCanHandleClosureValidationGroups()
634+
{
635+
$graphWalker = $this->getMockGraphWalker();
636+
$metadataFactory = $this->getMockMetadataFactory();
637+
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
638+
$object = $this->getMock('\stdClass');
639+
$form = $this->getBuilder()
640+
->setAttribute('validation_groups', function(FormInterface $form){
641+
return array('group1', 'group2');
642+
})
643+
->getForm();
644+
645+
$graphWalker->expects($this->at(0))
646+
->method('walkReference')
647+
->with($object, 'group1', 'data', true);
648+
$graphWalker->expects($this->at(1))
649+
->method('walkReference')
650+
->with($object, 'group2', 'data', true);
651+
652+
$form->setData($object);
653+
654+
DelegatingValidator::validateFormData($form, $context);
655+
}
656+
600657
public function testValidateFormDataUsesInheritedValidationGroup()
601658
{
602659
$graphWalker = $this->getMockGraphWalker();
@@ -622,6 +679,64 @@ public function testValidateFormDataUsesInheritedValidationGroup()
622679
DelegatingValidator::validateFormData($child, $context);
623680
}
624681

682+
public function testValidateFormDataUsesInheritedCallbackValidationGroup()
683+
{
684+
$graphWalker = $this->getMockGraphWalker();
685+
$metadataFactory = $this->getMockMetadataFactory();
686+
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
687+
$context->setPropertyPath('path');
688+
$object = $this->getMock('\stdClass');
689+
690+
$parent = $this->getBuilder()
691+
->setAttribute('validation_groups', array($this, 'getValidationGroups'))
692+
->getForm();
693+
$child = $this->getBuilder()
694+
->setAttribute('validation_groups', null)
695+
->getForm();
696+
$parent->add($child);
697+
698+
$child->setData($object);
699+
700+
$graphWalker->expects($this->at(0))
701+
->method('walkReference')
702+
->with($object, 'group1', 'path.data', true);
703+
$graphWalker->expects($this->at(1))
704+
->method('walkReference')
705+
->with($object, 'group2', 'path.data', true);
706+
707+
DelegatingValidator::validateFormData($child, $context);
708+
}
709+
710+
public function testValidateFormDataUsesInheritedClosureValidationGroup()
711+
{
712+
$graphWalker = $this->getMockGraphWalker();
713+
$metadataFactory = $this->getMockMetadataFactory();
714+
$context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
715+
$context->setPropertyPath('path');
716+
$object = $this->getMock('\stdClass');
717+
718+
$parent = $this->getBuilder()
719+
->setAttribute('validation_groups', function(FormInterface $form){
720+
return array('group1', 'group2');
721+
})
722+
->getForm();
723+
$child = $this->getBuilder()
724+
->setAttribute('validation_groups', null)
725+
->getForm();
726+
$parent->add($child);
727+
728+
$child->setData($object);
729+
730+
$graphWalker->expects($this->at(0))
731+
->method('walkReference')
732+
->with($object, 'group1', 'path.data', true);
733+
$graphWalker->expects($this->at(1))
734+
->method('walkReference')
735+
->with($object, 'group2', 'path.data', true);
736+
737+
DelegatingValidator::validateFormData($child, $context);
738+
}
739+
625740
public function testValidateFormDataAppendsPropertyPath()
626741
{
627742
$graphWalker = $this->getMockGraphWalker();

0 commit comments

Comments
 (0)
0