diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php index 6fdd2345ec..2595101374 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php @@ -27,9 +27,13 @@ class FieldTypeValidatorExtension extends AbstractTypeExtension public function buildForm(FormBuilder $builder, array $options) { - $options['validation_groups'] = empty($options['validation_groups']) - ? null - : (array)$options['validation_groups']; + if (empty($options['validation_groups'])) { + $options['validation_groups'] = null; + } else { + $options['validation_groups'] = is_callable($options['validation_groups']) + ? $options['validation_groups'] + : (array) $options['validation_groups']; + } $builder ->setAttribute('validation_groups', $options['validation_groups']) diff --git a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php index 6f8fc84c29..0c585c881d 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php @@ -133,6 +133,10 @@ class DelegatingValidator implements FormValidatorInterface if ($form->hasAttribute('validation_groups')) { $groups = $form->getAttribute('validation_groups'); + + if (is_callable($groups)) { + $groups = (array) call_user_func($groups, $form); + } } $currentForm = $form; @@ -141,6 +145,10 @@ class DelegatingValidator implements FormValidatorInterface if ($currentForm->hasAttribute('validation_groups')) { $groups = $currentForm->getAttribute('validation_groups'); + + if (is_callable($groups)) { + $groups = (array) call_user_func($groups, $currentForm); + } } } diff --git a/tests/Symfony/Tests/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php b/tests/Symfony/Tests/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php index feb6abd33f..5e0ee05567 100644 --- a/tests/Symfony/Tests/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php +++ b/tests/Symfony/Tests/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php @@ -38,6 +38,24 @@ class FieldTypeValidatorExtensionTest extends TypeTestCase $this->assertEquals(array('group1', 'group2'), $form->getAttribute('validation_groups')); } + public function testValidationGroupsCanBeSetToCallback() + { + $form = $this->factory->create('field', null, array( + 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), + )); + + $this->assertTrue(is_callable($form->getAttribute('validation_groups'))); + } + + public function testValidationGroupsCanBeSetToClosure() + { + $form = $this->factory->create('field', null, array( + 'validation_groups' => function($data, $extraData){ return null; }, + )); + + $this->assertTrue(is_callable($form->getAttribute('validation_groups'))); + } + public function testBindValidatesData() { $builder = $this->factory->createBuilder('field', null, array( diff --git a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php index 4316cf0344..22701304a6 100644 --- a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php +++ b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php @@ -11,6 +11,7 @@ namespace Symfony\Tests\Component\Form\Extension\Validator\Validator; +use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormError; use Symfony\Component\Form\Util\PropertyPath; @@ -90,6 +91,16 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase return $this->getMock('Symfony\Tests\Component\Form\FormInterface'); } + /** + * Access has to be public, as this method is called via callback array + * in {@link testValidateFormDataCanHandleCallbackValidationGroups()} + * and {@link testValidateFormDataUsesInheritedCallbackValidationGroup()} + */ + public function getValidationGroups(FormInterface $form) + { + return array('group1', 'group2'); + } + public function testUseValidateValueWhenValidationConstraintExist() { $constraint = $this->getMockForAbstractClass('Symfony\Component\Validator\Constraint'); @@ -597,6 +608,52 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase DelegatingValidator::validateFormData($form, $context); } + public function testValidateFormDataCanHandleCallbackValidationGroups() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $object = $this->getMock('\stdClass'); + $form = $this->getBuilder() + ->setAttribute('validation_groups', array($this, 'getValidationGroups')) + ->getForm(); + + $graphWalker->expects($this->at(0)) + ->method('walkReference') + ->with($object, 'group1', 'data', true); + $graphWalker->expects($this->at(1)) + ->method('walkReference') + ->with($object, 'group2', 'data', true); + + $form->setData($object); + + DelegatingValidator::validateFormData($form, $context); + } + + public function testValidateFormDataCanHandleClosureValidationGroups() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $object = $this->getMock('\stdClass'); + $form = $this->getBuilder() + ->setAttribute('validation_groups', function(FormInterface $form){ + return array('group1', 'group2'); + }) + ->getForm(); + + $graphWalker->expects($this->at(0)) + ->method('walkReference') + ->with($object, 'group1', 'data', true); + $graphWalker->expects($this->at(1)) + ->method('walkReference') + ->with($object, 'group2', 'data', true); + + $form->setData($object); + + DelegatingValidator::validateFormData($form, $context); + } + public function testValidateFormDataUsesInheritedValidationGroup() { $graphWalker = $this->getMockGraphWalker(); @@ -622,6 +679,64 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase DelegatingValidator::validateFormData($child, $context); } + public function testValidateFormDataUsesInheritedCallbackValidationGroup() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context->setPropertyPath('path'); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder() + ->setAttribute('validation_groups', array($this, 'getValidationGroups')) + ->getForm(); + $child = $this->getBuilder() + ->setAttribute('validation_groups', null) + ->getForm(); + $parent->add($child); + + $child->setData($object); + + $graphWalker->expects($this->at(0)) + ->method('walkReference') + ->with($object, 'group1', 'path.data', true); + $graphWalker->expects($this->at(1)) + ->method('walkReference') + ->with($object, 'group2', 'path.data', true); + + DelegatingValidator::validateFormData($child, $context); + } + + public function testValidateFormDataUsesInheritedClosureValidationGroup() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context->setPropertyPath('path'); + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder() + ->setAttribute('validation_groups', function(FormInterface $form){ + return array('group1', 'group2'); + }) + ->getForm(); + $child = $this->getBuilder() + ->setAttribute('validation_groups', null) + ->getForm(); + $parent->add($child); + + $child->setData($object); + + $graphWalker->expects($this->at(0)) + ->method('walkReference') + ->with($object, 'group1', 'path.data', true); + $graphWalker->expects($this->at(1)) + ->method('walkReference') + ->with($object, 'group2', 'path.data', true); + + DelegatingValidator::validateFormData($child, $context); + } + public function testValidateFormDataAppendsPropertyPath() { $graphWalker = $this->getMockGraphWalker();