Permalink
Browse files

merged branch bschussek/optional-form-child-validation (PR #3128)

Commits
-------

0c70a41 [Form] Made validation of form children configurable. Set the option "cascade_validation" to `true` if you need it.

Discussion
----------

[Form] Made validation of form children configurable

Bug fix: yes
Feature addition: yes
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #797
Todo: adapt documentation

![Travis Build Status](https://secure.travis-ci.org/bschussek/symfony.png?branch=optional-form-child-validation)

Child forms now aren't validated anymore by default. This is not a problem as long as @Valid constraints are properly put in your model. If you want to enable cascading validation, for example when there is no connection between the parent and the child model, you can set the option "cascade_validation" in the parent form to true.

This change is not backwards compatible, but from my estimation the break should not affect many applications.

---------------------------------------------------------------------------

by kriswallsmith at 2012-01-16T19:59:25Z

👍
  • Loading branch information...
2 parents efada56 + 0c70a41 commit b8d8cab1f98a8b3491be57109f9de1ee5a802363 @fabpot fabpot committed Jan 16, 2012
@@ -38,6 +38,7 @@ public function buildForm(FormBuilder $builder, array $options)
$builder
->setAttribute('validation_groups', $options['validation_groups'])
->setAttribute('validation_constraint', $options['validation_constraint'])
+ ->setAttribute('cascade_validation', $options['cascade_validation'])
->addValidator(new DelegatingValidator($this->validator));
}
@@ -46,6 +47,7 @@ public function getDefaultOptions(array $options)
return array(
'validation_groups' => null,
'validation_constraint' => null,
+ 'cascade_validation' => false,
@Gladhon
Gladhon Feb 1, 2012

It would be very nice, if the default options are configurable. Because it would be very easy to fix backwards compatible breaks here.

@webmozart
webmozart Feb 1, 2012 Symfony member

You can override the validator extension with a custom one. Create an extension of the class where you override getDefaultOptions() and register it in the DIC using the same tag as the class bundled in the core. Then it should be used instead AFAIK.

);
}
@@ -127,6 +127,29 @@ static public function validateFormData(FormInterface $form, ExecutionContext $c
}
}
+ static public function validateFormChildren(FormInterface $form, ExecutionContext $context)
+ {
+ if ($form->getAttribute('cascade_validation')) {
+ $propertyPath = $context->getPropertyPath();
+ $graphWalker = $context->getGraphWalker();
+
+ // The Execute constraint is called on class level, so we need to
+ // set the property manually
+ $context->setCurrentProperty('children');
+
+ // Adjust the property path accordingly
+ if (!empty($propertyPath)) {
+ $propertyPath .= '.';
+ }
+
+ $propertyPath .= 'children';
+
+ foreach (self::getFormValidationGroups($form) as $group) {
+ $graphWalker->walkReference($form->getChildren(), $group, $propertyPath, true);
+ }
+ }
+ }
+
static protected function getFormValidationGroups(FormInterface $form)
{
$groups = null;
@@ -10,9 +10,10 @@
<value>Symfony\Component\Form\Extension\Validator\Validator\DelegatingValidator</value>
<value>validateFormData</value>
</value>
+ <value>
+ <value>Symfony\Component\Form\Extension\Validator\Validator\DelegatingValidator</value>
+ <value>validateFormChildren</value>
+ </value>
</constraint>
- <property name="children">
- <constraint name="Valid" />
- </property>
</class>
</constraint-mapping>
@@ -798,6 +798,81 @@ public function testValidateFormDataDoesNotWalkScalars()
DelegatingValidator::validateFormData($form, $context);
}
+ public function testValidateFormChildren()
+ {
+ $graphWalker = $this->getMockGraphWalker();
+ $metadataFactory = $this->getMockMetadataFactory();
+ $context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
+ $form = $this->getBuilder()
+ ->setAttribute('cascade_validation', true)
+ ->setAttribute('validation_groups', array('group1', 'group2'))
+ ->getForm();
+ $form->add($this->getForm('firstName'));
+
+ $graphWalker->expects($this->at(0))
+ ->method('walkReference')
+ ->with($form->getChildren(), 'group1', 'children', true);
+ $graphWalker->expects($this->at(1))
+ ->method('walkReference')
+ ->with($form->getChildren(), 'group2', 'children', true);
+
+ DelegatingValidator::validateFormChildren($form, $context);
+ }
+
+ public function testValidateFormChildrenAppendsPropertyPath()
+ {
+ $graphWalker = $this->getMockGraphWalker();
+ $metadataFactory = $this->getMockMetadataFactory();
+ $context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
+ $context->setPropertyPath('path');
+ $form = $this->getBuilder()
+ ->setAttribute('cascade_validation', true)
+ ->getForm();
+ $form->add($this->getForm('firstName'));
+
+ $graphWalker->expects($this->once())
+ ->method('walkReference')
+ ->with($form->getChildren(), 'Default', 'path.children', true);
+
+ DelegatingValidator::validateFormChildren($form, $context);
+ }
+
+ public function testValidateFormChildrenSetsCurrentPropertyToData()
+ {
+ $graphWalker = $this->getMockGraphWalker();
+ $metadataFactory = $this->getMockMetadataFactory();
+ $context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
+ $form = $this->getBuilder()
+ ->setAttribute('cascade_validation', true)
+ ->getForm();
+ $form->add($this->getForm('firstName'));
+ $test = $this;
+
+ $graphWalker->expects($this->once())
+ ->method('walkReference')
+ ->will($this->returnCallback(function () use ($context, $test) {
+ $test->assertEquals('children', $context->getCurrentProperty());
+ }));
+
+ DelegatingValidator::validateFormChildren($form, $context);
+ }
+
+ public function testValidateFormChildrenDoesNothingIfDisabled()
+ {
+ $graphWalker = $this->getMockGraphWalker();
+ $metadataFactory = $this->getMockMetadataFactory();
+ $context = new ExecutionContext('Root', $graphWalker, $metadataFactory);
+ $form = $this->getBuilder()
+ ->setAttribute('cascade_validation', false)
+ ->getForm();
+ $form->add($this->getForm('firstName'));
+
+ $graphWalker->expects($this->never())
+ ->method('walkReference');
+
+ DelegatingValidator::validateFormChildren($form, $context);
+ }
+
public function testValidateIgnoresNonRoot()
{
$form = $this->getMockForm();

0 comments on commit b8d8cab

Please sign in to comment.