[Form] Made submit buttons able to convey validation groups

This commit is contained in:
Bernhard Schussek 2013-01-02 18:51:49 +01:00
parent cc2118dd5c
commit 7b438a816b
8 changed files with 209 additions and 10 deletions

View File

@ -163,5 +163,8 @@
<service id="form.type_extension.repeated.validator" class="Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension">
<tag name="form.type_extension" alias="repeated" />
</service>
<service id="form.type_extension.submit.validator" class="Symfony\Component\Form\Extension\Validator\Type\SubmitTypeValidatorExtension">
<tag name="form.type_extension" alias="submit" />
</service>
</services>
</container>

View File

@ -40,8 +40,6 @@ abstract class BaseType extends AbstractType
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
/* @var \Symfony\Component\Form\ClickableInterface $form */
$name = $form->getName();
$blockName = $options['block_name'] ?: $form->getName();
$translationDomain = $options['translation_domain'];

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Form\Extension\Validator\Constraints;
use Symfony\Component\Form\ClickableInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
use Symfony\Component\Validator\Constraint;
@ -171,15 +172,21 @@ class FormValidator extends ConstraintValidator
*/
private static function getValidationGroups(FormInterface $form)
{
$button = self::findClickedButton($form->getRoot());
if (null !== $button) {
$groups = $button->getConfig()->getOption('validation_groups');
if (null !== $groups) {
return self::resolveValidationGroups($groups, $form);
}
}
do {
$groups = $form->getConfig()->getOption('validation_groups');
if (null !== $groups) {
if (is_callable($groups)) {
$groups = call_user_func($groups, $form);
}
return (array) $groups;
return self::resolveValidationGroups($groups, $form);
}
$form = $form->getParent();
@ -187,4 +194,43 @@ class FormValidator extends ConstraintValidator
return array(Constraint::DEFAULT_GROUP);
}
/**
* Extracts a clicked button from a form tree, if one exists.
*
* @param FormInterface $form The root form.
*
* @return ClickableInterface|null The clicked button or null.
*/
private static function findClickedButton(FormInterface $form)
{
if ($form instanceof ClickableInterface && $form->isClicked()) {
return $form;
}
foreach ($form as $child) {
if (null !== ($button = self::findClickedButton($child))) {
return $button;
}
}
return null;
}
/**
* Post-processes the validation groups option for a given form.
*
* @param array|callable $groups The validation groups.
* @param FormInterface $form The validated form.
*
* @return array The validation groups.
*/
private static function resolveValidationGroups($groups, FormInterface $form)
{
if (is_callable($groups)) {
$groups = call_user_func($groups, $form);
}
return (array) $groups;
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Validator\Type;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* Encapsulates common logic of {@link FormTypeValidatorExtension} and
* {@link SubmitTypeValidatorExtension}.
*
* @author Bernhard Schussek <bschussek@gmail.com>
*/
abstract class BaseValidatorExtension extends AbstractTypeExtension
{
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
// Make sure that validation groups end up as null, closure or array
$validationGroupsNormalizer = function (Options $options, $groups) {
if (empty($groups)) {
return null;
}
if (is_callable($groups)) {
return $groups;
}
return (array) $groups;
};
$resolver->setDefaults(array(
'validation_groups' => null,
));
$resolver->setNormalizers(array(
'validation_groups' => $validationGroupsNormalizer,
));
}
}

View File

@ -22,7 +22,7 @@ use Symfony\Component\OptionsResolver\OptionsResolverInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormTypeValidatorExtension extends AbstractTypeExtension
class FormTypeValidatorExtension extends BaseValidatorExtension
{
/**
* @var ValidatorInterface
@ -53,6 +53,8 @@ class FormTypeValidatorExtension extends AbstractTypeExtension
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
// BC clause
$constraints = function (Options $options) {
return $options['validation_constraint'];

View File

@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Validator\Type;
use Symfony\Component\Form\AbstractTypeExtension;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class SubmitTypeValidatorExtension extends AbstractTypeExtension
{
/**
* {@inheritdoc}
*/
public function getExtendedType()
{
return 'submit';
}
}

View File

@ -51,6 +51,7 @@ class ValidatorExtension extends AbstractExtension
return array(
new Type\FormTypeValidatorExtension($this->validator),
new Type\RepeatedTypeValidatorExtension(),
new Type\SubmitTypeValidatorExtension(),
);
}
}

View File

@ -17,6 +17,7 @@ use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
use Symfony\Component\Form\SubmitButtonBuilder;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\NotNull;
use Symfony\Component\Validator\Constraints\NotBlank;
@ -313,6 +314,62 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
$this->validator->validate($form, new Form());
}
public function testUseValidationGroupOfClickedButton()
{
$context = $this->getMockExecutionContext();
$object = $this->getMock('\stdClass');
$parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
->setCompound(true)
->setDataMapper($this->getDataMapper())
->getForm();
$form = $this->getForm('name', '\stdClass', array(
'validation_groups' => 'form_group',
));
$parent->add($form);
$parent->add($this->getClickedSubmitButton('submit', array(
'validation_groups' => 'button_group',
)));
$form->setData($object);
$context->expects($this->once())
->method('validate')
->with($object, 'data', 'button_group', true);
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
}
public function testDontUseValidationGroupOfUnclickedButton()
{
$context = $this->getMockExecutionContext();
$object = $this->getMock('\stdClass');
$parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
->setCompound(true)
->setDataMapper($this->getDataMapper())
->getForm();
$form = $this->getForm('name', '\stdClass', array(
'validation_groups' => 'form_group',
));
$parent->add($form);
$parent->add($this->getSubmitButton('submit', array(
'validation_groups' => 'button_group',
)));
$form->setData($object);
$context->expects($this->once())
->method('validate')
->with($object, 'data', 'form_group', true);
$this->validator->initialize($context);
$this->validator->validate($form, new Form());
}
public function testUseInheritedValidationGroup()
{
$context = $this->getMockExecutionContext();
@ -561,9 +618,21 @@ class FormValidatorTest extends \PHPUnit_Framework_TestCase
return new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory, $options);
}
private function getForm($name = 'name', $dataClass = null)
private function getForm($name = 'name', $dataClass = null, array $options = array())
{
return $this->getBuilder($name, $dataClass)->getForm();
return $this->getBuilder($name, $dataClass, $options)->getForm();
}
private function getSubmitButton($name = 'name', array $options = array())
{
$builder = new SubmitButtonBuilder($name, $options);
return $builder->getForm();
}
private function getClickedSubmitButton($name = 'name', array $options = array())
{
return $this->getSubmitButton($name, $options)->bind('');
}
/**