merged branch canni/enable_validation_groups_callback (PR #2498)
Commits -------d08ec5e
Add DelegatingValidator testse1822e7
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.
This commit is contained in:
commit
4f3e7bb698
@ -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'])
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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();
|
||||
|
Reference in New Issue
Block a user