[Form] Allowed native framework errors to be mapped as well
This commit is contained in:
parent
59d6b55137
commit
ac6939441f
|
@ -539,6 +539,26 @@
|
||||||
$form->getConfig()->getErrorBubbling();
|
$form->getConfig()->getErrorBubbling();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* The option "validation_constraint" is deprecated and will be removed
|
||||||
|
in Symfony 2.3. You should use the option "constraints" instead,
|
||||||
|
where you can pass one or more constraints for a form.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
$builder->add('name', 'text', array(
|
||||||
|
'validation_constraint' => new NotBlank(),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
After (if the address object is an array):
|
||||||
|
|
||||||
|
```
|
||||||
|
$builder->add('name', 'text', array(
|
||||||
|
'constraints' => new NotBlank(),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
### Validator
|
### Validator
|
||||||
|
|
||||||
* The methods `setMessage()`, `getMessageTemplate()` and
|
* The methods `setMessage()`, `getMessageTemplate()` and
|
||||||
|
|
|
@ -133,9 +133,12 @@
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- FormTypeValidatorExtension -->
|
<!-- FormTypeValidatorExtension -->
|
||||||
<service id="form.type_extension.field" class="Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension">
|
<service id="form.type_extension.form.validator" class="Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension">
|
||||||
<tag name="form.type_extension" alias="form" />
|
<tag name="form.type_extension" alias="form" />
|
||||||
<argument type="service" id="validator" />
|
<argument type="service" id="validator" />
|
||||||
</service>
|
</service>
|
||||||
|
<service id="form.type_extension.repeated.validator" class="Symfony\Component\Form\Extension\Validator\Type\RepeatedTypeValidatorExtension">
|
||||||
|
<tag name="form.type_extension" alias="repeated" />
|
||||||
|
</service>
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
|
|
@ -83,3 +83,5 @@ CHANGELOG
|
||||||
* `getErrorBubbling`
|
* `getErrorBubbling`
|
||||||
* `getNormTransformers`
|
* `getNormTransformers`
|
||||||
* `getClientTransformers`
|
* `getClientTransformers`
|
||||||
|
* deprecated the option "validation_constraint" in favor of the new
|
||||||
|
option "constraints"
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?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\Core\EventListener;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\FormError;
|
|
||||||
use Symfony\Component\Form\FormEvents;
|
|
||||||
use Symfony\Component\Form\Event\DataEvent;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
|
||||||
*/
|
|
||||||
class ValidationListener implements EventSubscriberInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
static public function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return array(FormEvents::POST_BIND => 'validateForm');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateForm(DataEvent $event)
|
|
||||||
{
|
|
||||||
$form = $event->getForm();
|
|
||||||
|
|
||||||
if (!$form->isSynchronized()) {
|
|
||||||
$form->addError(new FormError(
|
|
||||||
$form->getAttribute('invalid_message'),
|
|
||||||
$form->getAttribute('invalid_message_parameters')
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($form->getExtraData()) > 0) {
|
|
||||||
$form->addError(new FormError('This form should not contain extra fields.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($form->isRoot() && isset($_SERVER['CONTENT_LENGTH'])) {
|
|
||||||
$length = (int) $_SERVER['CONTENT_LENGTH'];
|
|
||||||
$max = trim(ini_get('post_max_size'));
|
|
||||||
|
|
||||||
if ('' !== $max) {
|
|
||||||
switch (strtolower(substr($max, -1))) {
|
|
||||||
// The 'G' modifier is available since PHP 5.1.0
|
|
||||||
case 'g':
|
|
||||||
$max *= 1024;
|
|
||||||
case 'm':
|
|
||||||
$max *= 1024;
|
|
||||||
case 'k':
|
|
||||||
$max *= 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($length > $max) {
|
|
||||||
$form->addError(new FormError('The uploaded file was too large. Please try to upload a smaller file'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -51,8 +51,6 @@ class DateTimeType extends AbstractType
|
||||||
'days',
|
'days',
|
||||||
'empty_value',
|
'empty_value',
|
||||||
'required',
|
'required',
|
||||||
'invalid_message',
|
|
||||||
'invalid_message_parameters',
|
|
||||||
'translation_domain',
|
'translation_domain',
|
||||||
)));
|
)));
|
||||||
$timeOptions = array_intersect_key($options, array_flip(array(
|
$timeOptions = array_intersect_key($options, array_flip(array(
|
||||||
|
@ -62,8 +60,6 @@ class DateTimeType extends AbstractType
|
||||||
'with_seconds',
|
'with_seconds',
|
||||||
'empty_value',
|
'empty_value',
|
||||||
'required',
|
'required',
|
||||||
'invalid_message',
|
|
||||||
'invalid_message_parameters',
|
|
||||||
'translation_domain',
|
'translation_domain',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\FormFactoryInterface;
|
use Symfony\Component\Form\FormFactoryInterface;
|
||||||
use Symfony\Component\Form\FormView;
|
use Symfony\Component\Form\FormView;
|
||||||
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
|
use Symfony\Component\Form\Extension\Core\EventListener\TrimListener;
|
||||||
use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener;
|
|
||||||
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
|
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\Form\Exception\FormException;
|
use Symfony\Component\Form\Exception\FormException;
|
||||||
|
@ -50,19 +49,15 @@ class FormType extends AbstractType
|
||||||
->setVirtual($options['virtual'])
|
->setVirtual($options['virtual'])
|
||||||
->setAttribute('read_only', $options['read_only'])
|
->setAttribute('read_only', $options['read_only'])
|
||||||
->setAttribute('by_reference', $options['by_reference'])
|
->setAttribute('by_reference', $options['by_reference'])
|
||||||
->setAttribute('error_mapping', $options['error_mapping'])
|
|
||||||
->setAttribute('max_length', $options['max_length'])
|
->setAttribute('max_length', $options['max_length'])
|
||||||
->setAttribute('pattern', $options['pattern'])
|
->setAttribute('pattern', $options['pattern'])
|
||||||
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
|
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
|
||||||
->setAttribute('attr', $options['attr'])
|
->setAttribute('attr', $options['attr'])
|
||||||
->setAttribute('label_attr', $options['label_attr'])
|
->setAttribute('label_attr', $options['label_attr'])
|
||||||
->setAttribute('invalid_message', $options['invalid_message'])
|
|
||||||
->setAttribute('invalid_message_parameters', $options['invalid_message_parameters'])
|
|
||||||
->setAttribute('translation_domain', $options['translation_domain'])
|
->setAttribute('translation_domain', $options['translation_domain'])
|
||||||
->setAttribute('single_control', $options['single_control'])
|
->setAttribute('single_control', $options['single_control'])
|
||||||
->setData($options['data'])
|
->setData($options['data'])
|
||||||
->setDataMapper(new PropertyPathMapper())
|
->setDataMapper(new PropertyPathMapper())
|
||||||
->addEventSubscriber(new ValidationListener())
|
|
||||||
;
|
;
|
||||||
|
|
||||||
if ($options['trim']) {
|
if ($options['trim']) {
|
||||||
|
@ -197,27 +192,24 @@ class FormType extends AbstractType
|
||||||
};
|
};
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'data' => null,
|
'data' => null,
|
||||||
'data_class' => $dataClass,
|
'data_class' => $dataClass,
|
||||||
'empty_data' => $emptyData,
|
'empty_data' => $emptyData,
|
||||||
'trim' => true,
|
'trim' => true,
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'read_only' => false,
|
'read_only' => false,
|
||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
'max_length' => null,
|
'max_length' => null,
|
||||||
'pattern' => null,
|
'pattern' => null,
|
||||||
'property_path' => null,
|
'property_path' => null,
|
||||||
'mapped' => $mapped,
|
'mapped' => $mapped,
|
||||||
'by_reference' => true,
|
'by_reference' => true,
|
||||||
'error_bubbling' => $errorBubbling,
|
'error_bubbling' => $errorBubbling,
|
||||||
'error_mapping' => array(),
|
'label' => null,
|
||||||
'label' => null,
|
'attr' => array(),
|
||||||
'attr' => array(),
|
'label_attr' => array(),
|
||||||
'label_attr' => array(),
|
'virtual' => false,
|
||||||
'virtual' => false,
|
'single_control' => false,
|
||||||
'single_control' => false,
|
|
||||||
'invalid_message' => 'This value is not valid.',
|
|
||||||
'invalid_message_parameters' => array(),
|
|
||||||
'translation_domain' => 'messages',
|
'translation_domain' => 'messages',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,6 @@ class RepeatedType extends AbstractType
|
||||||
*/
|
*/
|
||||||
public function getDefaultOptions()
|
public function getDefaultOptions()
|
||||||
{
|
{
|
||||||
// Map errors to the first field
|
|
||||||
$errorMapping = function (Options $options) {
|
|
||||||
return array('.' => $options['first_name']);
|
|
||||||
};
|
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'options' => array(),
|
'options' => array(),
|
||||||
|
@ -55,7 +50,6 @@ class RepeatedType extends AbstractType
|
||||||
'first_name' => 'first',
|
'first_name' => 'first',
|
||||||
'second_name' => 'second',
|
'second_name' => 'second',
|
||||||
'error_bubbling' => false,
|
'error_bubbling' => false,
|
||||||
'error_mapping' => $errorMapping,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?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\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class Form extends Constraint
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Violation code marking an invalid form.
|
||||||
|
*/
|
||||||
|
const ERR_INVALID = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getTargets()
|
||||||
|
{
|
||||||
|
return self::CLASS_CONSTRAINT;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
<?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\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\Util\ServerParams;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class FormValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ServerParams
|
||||||
|
*/
|
||||||
|
private $serverParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a validator with the given server parameters.
|
||||||
|
*
|
||||||
|
* @param ServerParams $params The server parameters. Default
|
||||||
|
* parameters are created if null.
|
||||||
|
*/
|
||||||
|
public function __construct(ServerParams $params = null)
|
||||||
|
{
|
||||||
|
if (null === $params) {
|
||||||
|
$params = new ServerParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->serverParams = $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validate($form, Constraint $constraint)
|
||||||
|
{
|
||||||
|
if (!$form instanceof FormInterface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @var FormInterface $form */
|
||||||
|
|
||||||
|
$path = $this->context->getPropertyPath();
|
||||||
|
$graphWalker = $this->context->getGraphWalker();
|
||||||
|
$groups = $this->getValidationGroups($form);
|
||||||
|
|
||||||
|
if (!empty($path)) {
|
||||||
|
$path .= '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($form->isSynchronized()) {
|
||||||
|
// Validate the form data only if transformation succeeded
|
||||||
|
|
||||||
|
// Validate the data against its own constraints
|
||||||
|
if (self::allowDataWalking($form)) {
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$graphWalker->walkReference($form->getData(), $group, $path . 'data', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the data against the constraints defined
|
||||||
|
// in the form
|
||||||
|
$constraints = $form->getAttribute('constraints');
|
||||||
|
foreach ($constraints as $constraint) {
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$graphWalker->walkConstraint($constraint, $form->getData(), $group, $path . 'data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Mark the form with an error if it is not synchronized
|
||||||
|
$this->context->addViolation(
|
||||||
|
$form->getAttribute('invalid_message'),
|
||||||
|
array('{{ value }}' => (string) $form->getClientData()),
|
||||||
|
$form->getClientData(),
|
||||||
|
null,
|
||||||
|
Form::ERR_INVALID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the form with an error if it contains extra fields
|
||||||
|
if (count($form->getExtraData()) > 0) {
|
||||||
|
$this->context->addViolation(
|
||||||
|
$form->getAttribute('extra_fields_message'),
|
||||||
|
array('{{ extra_fields }}' => implode('", "', array_keys($form->getExtraData()))),
|
||||||
|
$form->getExtraData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the form with an error if the uploaded size was too large
|
||||||
|
$length = $this->serverParams->getContentLength();
|
||||||
|
|
||||||
|
if ($form->isRoot() && null !== $length) {
|
||||||
|
$max = strtoupper(trim($this->serverParams->getPostMaxSize()));
|
||||||
|
|
||||||
|
if ('' !== $max) {
|
||||||
|
$maxLength = (int) $max;
|
||||||
|
|
||||||
|
switch (substr($max, -1)) {
|
||||||
|
// The 'G' modifier is available since PHP 5.1.0
|
||||||
|
case 'G':
|
||||||
|
$maxLength *= pow(1024, 3);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
$maxLength *= pow(1024, 2);
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
$maxLength *= 1024;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($length > $maxLength) {
|
||||||
|
$this->context->addViolation(
|
||||||
|
$form->getAttribute('post_max_size_message'),
|
||||||
|
array('{{ max }}' => $max),
|
||||||
|
$length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the data of a form may be walked.
|
||||||
|
*
|
||||||
|
* @param FormInterface $form The form to test.
|
||||||
|
*
|
||||||
|
* @return Boolean Whether the graph walker may walk the data.
|
||||||
|
*/
|
||||||
|
private function allowDataWalking(FormInterface $form)
|
||||||
|
{
|
||||||
|
$data = $form->getData();
|
||||||
|
|
||||||
|
// Scalar values cannot have mapped constraints
|
||||||
|
if (!is_object($data) && !is_array($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root forms are always validated
|
||||||
|
if ($form->isRoot()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-root forms are validated if validation cascading
|
||||||
|
// is enabled in all ancestor forms
|
||||||
|
$parent = $form->getParent();
|
||||||
|
|
||||||
|
while (null !== $parent) {
|
||||||
|
if (!$parent->getAttribute('cascade_validation')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent = $parent->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the validation groups of the given form.
|
||||||
|
*
|
||||||
|
* @param FormInterface $form The form.
|
||||||
|
*
|
||||||
|
* @return array The validation groups.
|
||||||
|
*/
|
||||||
|
private function getValidationGroups(FormInterface $form)
|
||||||
|
{
|
||||||
|
$groups = null;
|
||||||
|
|
||||||
|
if ($form->hasAttribute('validation_groups')) {
|
||||||
|
$groups = $form->getAttribute('validation_groups');
|
||||||
|
|
||||||
|
if (is_callable($groups)) {
|
||||||
|
$groups = (array) call_user_func($groups, $form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentForm = $form;
|
||||||
|
while (!$groups && $currentForm->hasParent()) {
|
||||||
|
$currentForm = $currentForm->getParent();
|
||||||
|
|
||||||
|
if ($currentForm->hasAttribute('validation_groups')) {
|
||||||
|
$groups = $currentForm->getAttribute('validation_groups');
|
||||||
|
|
||||||
|
if (is_callable($groups)) {
|
||||||
|
$groups = (array) call_user_func($groups, $currentForm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $groups) {
|
||||||
|
$groups = array('Default');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (array) $groups;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,154 +0,0 @@
|
||||||
<?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\EventListener;
|
|
||||||
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\Form\FormError;
|
|
||||||
use Symfony\Component\Form\FormEvents;
|
|
||||||
use Symfony\Component\Form\Event\DataEvent;
|
|
||||||
use Symfony\Component\Form\Exception\FormException;
|
|
||||||
use Symfony\Component\Form\Util\PropertyPath;
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
|
||||||
use Symfony\Component\Validator\ValidatorInterface;
|
|
||||||
use Symfony\Component\Validator\ExecutionContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
|
||||||
*/
|
|
||||||
class DelegatingValidationListener implements EventSubscriberInterface
|
|
||||||
{
|
|
||||||
private $validator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
static public function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return array(FormEvents::POST_BIND => 'validateForm');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the data of a form
|
|
||||||
*
|
|
||||||
* This method is called automatically during the validation process.
|
|
||||||
*
|
|
||||||
* @param FormInterface $form The validated form
|
|
||||||
* @param ExecutionContext $context The current validation context
|
|
||||||
*/
|
|
||||||
static public function validateFormData(FormInterface $form, ExecutionContext $context)
|
|
||||||
{
|
|
||||||
if (is_object($form->getData()) || is_array($form->getData())) {
|
|
||||||
$propertyPath = $context->getPropertyPath();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
|
|
||||||
// Adjust the property path accordingly
|
|
||||||
if (!empty($propertyPath)) {
|
|
||||||
$propertyPath .= '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
$propertyPath .= 'data';
|
|
||||||
|
|
||||||
foreach (self::getFormValidationGroups($form) as $group) {
|
|
||||||
$graphWalker->walkReference($form->getData(), $group, $propertyPath, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static public function validateFormChildren(FormInterface $form, ExecutionContext $context)
|
|
||||||
{
|
|
||||||
if ($form->getAttribute('cascade_validation')) {
|
|
||||||
$propertyPath = $context->getPropertyPath();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
|
|
||||||
// Adjust the property path accordingly
|
|
||||||
if (!empty($propertyPath)) {
|
|
||||||
$propertyPath .= '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
$propertyPath .= 'children';
|
|
||||||
|
|
||||||
$graphWalker->walkReference($form->getChildren(), Constraint::DEFAULT_GROUP, $propertyPath, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static protected function getFormValidationGroups(FormInterface $form)
|
|
||||||
{
|
|
||||||
$groups = null;
|
|
||||||
|
|
||||||
if ($form->hasAttribute('validation_groups')) {
|
|
||||||
$groups = $form->getAttribute('validation_groups');
|
|
||||||
|
|
||||||
if (is_callable($groups)) {
|
|
||||||
$groups = (array) call_user_func($groups, $form);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentForm = $form;
|
|
||||||
while (!$groups && $currentForm->hasParent()) {
|
|
||||||
$currentForm = $currentForm->getParent();
|
|
||||||
|
|
||||||
if ($currentForm->hasAttribute('validation_groups')) {
|
|
||||||
$groups = $currentForm->getAttribute('validation_groups');
|
|
||||||
|
|
||||||
if (is_callable($groups)) {
|
|
||||||
$groups = (array) call_user_func($groups, $currentForm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null === $groups) {
|
|
||||||
$groups = array('Default');
|
|
||||||
}
|
|
||||||
|
|
||||||
return (array) $groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct(ValidatorInterface $validator)
|
|
||||||
{
|
|
||||||
$this->validator = $validator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the form and its domain object.
|
|
||||||
*
|
|
||||||
* @param DataEvent $event The event object
|
|
||||||
*/
|
|
||||||
public function validateForm(DataEvent $event)
|
|
||||||
{
|
|
||||||
$form = $event->getForm();
|
|
||||||
|
|
||||||
if ($form->isRoot()) {
|
|
||||||
// Validate the form in group "Default"
|
|
||||||
// Validation of the data in the custom group is done by validateData(),
|
|
||||||
// which is constrained by the Execute constraint
|
|
||||||
if ($form->hasAttribute('validation_constraint')) {
|
|
||||||
$violations = $this->validator->validateValue(
|
|
||||||
$form->getData(),
|
|
||||||
$form->getAttribute('validation_constraint'),
|
|
||||||
self::getFormValidationGroups($form)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$violations = $this->validator->validate($form);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($violations) > 0) {
|
|
||||||
$mapper = new ViolationMapper();
|
|
||||||
|
|
||||||
foreach ($violations as $violation) {
|
|
||||||
$mapper->mapViolation($violation, $form);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?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\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
|
use Symfony\Component\Form\FormEvents;
|
||||||
|
use Symfony\Component\Form\Event\DataEvent;
|
||||||
|
use Symfony\Component\Form\Exception\FormException;
|
||||||
|
use Symfony\Component\Form\Util\PropertyPath;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ValidatorInterface;
|
||||||
|
use Symfony\Component\Validator\ExecutionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class ValidationListener implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
private $violationMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
static public function getSubscribedEvents()
|
||||||
|
{
|
||||||
|
return array(FormEvents::POST_BIND => 'validateForm');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(ValidatorInterface $validator, ViolationMapperInterface $violationMapper)
|
||||||
|
{
|
||||||
|
$this->validator = $validator;
|
||||||
|
$this->violationMapper = $violationMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the form and its domain object.
|
||||||
|
*
|
||||||
|
* @param DataEvent $event The event object
|
||||||
|
*/
|
||||||
|
public function validateForm(DataEvent $event)
|
||||||
|
{
|
||||||
|
$form = $event->getForm();
|
||||||
|
|
||||||
|
if ($form->isRoot()) {
|
||||||
|
// Validate the form in group "Default"
|
||||||
|
$violations = $this->validator->validate($form);
|
||||||
|
|
||||||
|
if (count($violations) > 0) {
|
||||||
|
foreach ($violations as $violation) {
|
||||||
|
// Allow the "invalid" constraint to be put onto
|
||||||
|
// non-synchzronized forms
|
||||||
|
$allowNonSynchronized = Form::ERR_INVALID === $violation->getCode();
|
||||||
|
|
||||||
|
$this->violationMapper->mapViolation($violation, $form, $allowNonSynchronized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,21 +13,35 @@ namespace Symfony\Component\Form\Extension\Validator\Type;
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractTypeExtension;
|
use Symfony\Component\Form\AbstractTypeExtension;
|
||||||
use Symfony\Component\Form\FormBuilder;
|
use Symfony\Component\Form\FormBuilder;
|
||||||
use Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener;
|
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapper;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
|
||||||
use Symfony\Component\Validator\ValidatorInterface;
|
use Symfony\Component\Validator\ValidatorInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\Options;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
*/
|
*/
|
||||||
class FormTypeValidatorExtension extends AbstractTypeExtension
|
class FormTypeValidatorExtension extends AbstractTypeExtension
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var ValidatorInterface
|
||||||
|
*/
|
||||||
private $validator;
|
private $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ViolationMapper
|
||||||
|
*/
|
||||||
|
private $violationMapper;
|
||||||
|
|
||||||
public function __construct(ValidatorInterface $validator)
|
public function __construct(ValidatorInterface $validator)
|
||||||
{
|
{
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
|
$this->violationMapper = new ViolationMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function buildForm(FormBuilder $builder, array $options)
|
public function buildForm(FormBuilder $builder, array $options)
|
||||||
{
|
{
|
||||||
if (empty($options['validation_groups'])) {
|
if (empty($options['validation_groups'])) {
|
||||||
|
@ -38,23 +52,49 @@ class FormTypeValidatorExtension extends AbstractTypeExtension
|
||||||
: (array) $options['validation_groups'];
|
: (array) $options['validation_groups'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Objects, when casted to an array, are split into their properties
|
||||||
|
$constraints = is_object($options['constraints'])
|
||||||
|
? array($options['constraints'])
|
||||||
|
: (array) $options['constraints'];
|
||||||
|
|
||||||
$builder
|
$builder
|
||||||
|
->setAttribute('error_mapping', $options['error_mapping'])
|
||||||
->setAttribute('validation_groups', $options['validation_groups'])
|
->setAttribute('validation_groups', $options['validation_groups'])
|
||||||
->setAttribute('validation_constraint', $options['validation_constraint'])
|
->setAttribute('constraints', $constraints)
|
||||||
->setAttribute('cascade_validation', $options['cascade_validation'])
|
->setAttribute('cascade_validation', $options['cascade_validation'])
|
||||||
->addEventSubscriber(new DelegatingValidationListener($this->validator))
|
->setAttribute('invalid_message', $options['invalid_message'])
|
||||||
|
->setAttribute('extra_fields_message', $options['extra_fields_message'])
|
||||||
|
->setAttribute('post_max_size_message', $options['post_max_size_message'])
|
||||||
|
->addEventSubscriber(new ValidationListener($this->validator, $this->violationMapper))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function getDefaultOptions()
|
public function getDefaultOptions()
|
||||||
{
|
{
|
||||||
|
// BC clause
|
||||||
|
$constraints = function (Options $options) {
|
||||||
|
return $options['validation_constraint'];
|
||||||
|
};
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'validation_groups' => null,
|
'error_mapping' => array(),
|
||||||
|
'validation_groups' => null,
|
||||||
|
// "validation_constraint" is deprecated. Use "constraints".
|
||||||
'validation_constraint' => null,
|
'validation_constraint' => null,
|
||||||
'cascade_validation' => false,
|
'constraints' => $constraints,
|
||||||
|
'cascade_validation' => false,
|
||||||
|
'invalid_message' => 'This value is not valid.',
|
||||||
|
'extra_fields_message' => 'This form should not contain extra fields.',
|
||||||
|
'post_max_size_message' => 'The uploaded file was too large. Please try to upload a smaller file.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function getExtendedType()
|
public function getExtendedType()
|
||||||
{
|
{
|
||||||
return 'form';
|
return 'form';
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class RepeatedTypeValidatorExtension extends AbstractTypeExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getDefaultOptions()
|
||||||
|
{
|
||||||
|
// Map errors to the first field
|
||||||
|
$errorMapping = function (Options $options) {
|
||||||
|
return array('.' => $options['first_name']);
|
||||||
|
};
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'error_mapping' => $errorMapping,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getExtendedType()
|
||||||
|
{
|
||||||
|
return 'repeated';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?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\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class ServerParams
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the "post_max_size" ini setting.
|
||||||
|
*
|
||||||
|
* @return string The value of the ini setting.
|
||||||
|
*/
|
||||||
|
public function getPostMaxSize()
|
||||||
|
{
|
||||||
|
return ini_get('post_max_size');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content length of the request.
|
||||||
|
*
|
||||||
|
* @return mixed The request content length.
|
||||||
|
*/
|
||||||
|
public function getContentLength()
|
||||||
|
{
|
||||||
|
return isset($_SERVER['CONTENT_LENGTH'])
|
||||||
|
? (int) $_SERVER['CONTENT_LENGTH']
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,9 +12,9 @@
|
||||||
namespace Symfony\Component\Form\Extension\Validator;
|
namespace Symfony\Component\Form\Extension\Validator;
|
||||||
|
|
||||||
use Symfony\Component\Form\Extension\Validator\Type;
|
use Symfony\Component\Form\Extension\Validator\Type;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||||
use Symfony\Component\Form\AbstractExtension;
|
use Symfony\Component\Form\AbstractExtension;
|
||||||
use Symfony\Component\Validator\ValidatorInterface;
|
use Symfony\Component\Validator\ValidatorInterface;
|
||||||
use Symfony\Component\Validator\Constraints\Callback;
|
|
||||||
use Symfony\Component\Validator\Constraints\Valid;
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
|
|
||||||
class ValidatorExtension extends AbstractExtension
|
class ValidatorExtension extends AbstractExtension
|
||||||
|
@ -26,7 +26,7 @@ class ValidatorExtension extends AbstractExtension
|
||||||
$this->validator = $validator;
|
$this->validator = $validator;
|
||||||
|
|
||||||
$metadata = $this->validator->getMetadataFactory()->getClassMetadata('Symfony\Component\Form\Form');
|
$metadata = $this->validator->getMetadataFactory()->getClassMetadata('Symfony\Component\Form\Form');
|
||||||
$metadata->addConstraint(new Callback(array(array('Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener', 'validateFormData'))));
|
$metadata->addConstraint(new Form());
|
||||||
$metadata->addPropertyConstraint('children', new Valid());
|
$metadata->addPropertyConstraint('children', new Valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ class ValidatorExtension extends AbstractExtension
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
new Type\FormTypeValidatorExtension($this->validator),
|
new Type\FormTypeValidatorExtension($this->validator),
|
||||||
|
new Type\RepeatedTypeValidatorExtension(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ use Symfony\Component\Validator\ConstraintViolation;
|
||||||
/**
|
/**
|
||||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
*/
|
*/
|
||||||
class ViolationMapper
|
class ViolationMapper implements ViolationMapperInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var FormInterface
|
* @var FormInterface
|
||||||
|
@ -41,15 +41,17 @@ class ViolationMapper
|
||||||
private $rules = array();
|
private $rules = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a constraint violation to a form in the form tree under
|
* @var Boolean
|
||||||
* the given form.
|
|
||||||
*
|
|
||||||
* @param ConstraintViolation $violation The violation to map.
|
|
||||||
* @param FormInterface $form The root form of the tree
|
|
||||||
* to map it to.
|
|
||||||
*/
|
*/
|
||||||
public function mapViolation(ConstraintViolation $violation, FormInterface $form)
|
private $allowNonSynchronized;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false)
|
||||||
{
|
{
|
||||||
|
$this->allowNonSynchronized = $allowNonSynchronized;
|
||||||
|
|
||||||
$violationPath = new ViolationPath($violation->getPropertyPath());
|
$violationPath = new ViolationPath($violation->getPropertyPath());
|
||||||
$relativePath = $this->reconstructPath($violationPath, $form);
|
$relativePath = $this->reconstructPath($violationPath, $form);
|
||||||
$match = false;
|
$match = false;
|
||||||
|
@ -74,7 +76,7 @@ class ViolationMapper
|
||||||
$this->setScope($relativePath->getRoot());
|
$this->setScope($relativePath->getRoot());
|
||||||
$it = new PropertyPathIterator($relativePath);
|
$it = new PropertyPathIterator($relativePath);
|
||||||
|
|
||||||
while ($this->scope->isSynchronized() && null !== ($child = $this->matchChild($it))) {
|
while ($this->isValidScope() && null !== ($child = $this->matchChild($it))) {
|
||||||
$this->setScope($child);
|
$this->setScope($child);
|
||||||
$it->next();
|
$it->next();
|
||||||
$match = true;
|
$match = true;
|
||||||
|
@ -95,7 +97,7 @@ class ViolationMapper
|
||||||
// The overhead of setScope() is not needed anymore here
|
// The overhead of setScope() is not needed anymore here
|
||||||
$this->scope = $form;
|
$this->scope = $form;
|
||||||
|
|
||||||
while ($this->scope->isSynchronized() && $it->valid() && $it->mapsForm()) {
|
while ($this->isValidScope() && $it->valid() && $it->mapsForm()) {
|
||||||
if (!$this->scope->has($it->current())) {
|
if (!$this->scope->has($it->current())) {
|
||||||
// Break if we find a reference to a non-existing child
|
// Break if we find a reference to a non-existing child
|
||||||
break;
|
break;
|
||||||
|
@ -109,14 +111,14 @@ class ViolationMapper
|
||||||
// Follow dot rules until we have the final target
|
// Follow dot rules until we have the final target
|
||||||
$mapping = $this->scope->getAttribute('error_mapping');
|
$mapping = $this->scope->getAttribute('error_mapping');
|
||||||
|
|
||||||
while ($this->scope->isSynchronized() && isset($mapping['.'])) {
|
while ($this->isValidScope() && isset($mapping['.'])) {
|
||||||
$dotRule = new MappingRule($this->scope, '.', $mapping['.']);
|
$dotRule = new MappingRule($this->scope, '.', $mapping['.']);
|
||||||
$this->scope = $dotRule->getTarget();
|
$this->scope = $dotRule->getTarget();
|
||||||
$mapping = $this->scope->getAttribute('error_mapping');
|
$mapping = $this->scope->getAttribute('error_mapping');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add the error if the form is synchronized
|
// Only add the error if the form is synchronized
|
||||||
if ($this->scope->isSynchronized()) {
|
if ($this->isValidScope()) {
|
||||||
$this->scope->addError(new FormError(
|
$this->scope->addError(new FormError(
|
||||||
$violation->getMessageTemplate(),
|
$violation->getMessageTemplate(),
|
||||||
$violation->getMessageParameters(),
|
$violation->getMessageParameters(),
|
||||||
|
@ -286,4 +288,12 @@ class ViolationMapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
private function isValidScope()
|
||||||
|
{
|
||||||
|
return $this->allowNonSynchronized || $this->scope->isSynchronized();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?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\ViolationMapper;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
interface ViolationMapperInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Maps a constraint violation to a form in the form tree under
|
||||||
|
* the given form.
|
||||||
|
*
|
||||||
|
* @param ConstraintViolation $violation The violation to map.
|
||||||
|
* @param FormInterface $form The root form of the tree
|
||||||
|
* to map it to.
|
||||||
|
* @param Boolean $allowNonSynchronized Whether to allow
|
||||||
|
* mapping to non-synchronized forms.
|
||||||
|
*/
|
||||||
|
function mapViolation(ConstraintViolation $violation, FormInterface $form, $allowNonSynchronized = false);
|
||||||
|
}
|
|
@ -5,15 +5,9 @@
|
||||||
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
|
||||||
|
|
||||||
<class name="Symfony\Component\Form\Form">
|
<class name="Symfony\Component\Form\Form">
|
||||||
<constraint name="Callback">
|
<constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
|
||||||
<value>
|
<property name="children">
|
||||||
<value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
|
<constraint name="Valid" />
|
||||||
<value>validateFormData</value>
|
</property>
|
||||||
</value>
|
|
||||||
<value>
|
|
||||||
<value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
|
|
||||||
<value>validateFormChildren</value>
|
|
||||||
</value>
|
|
||||||
</constraint>
|
|
||||||
</class>
|
</class>
|
||||||
</constraint-mapping>
|
</constraint-mapping>
|
||||||
|
|
|
@ -226,32 +226,6 @@ class DateTimeTypeTest extends LocalizedTestCase
|
||||||
$this->assertDateTimeEquals($dateTime, $form->getData());
|
$this->assertDateTimeEquals($dateTime, $form->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubmit_invalidDateTime()
|
|
||||||
{
|
|
||||||
$form = $this->factory->create('datetime', null, array(
|
|
||||||
'invalid_message' => 'Customized invalid message',
|
|
||||||
// Only possible with the "text" widget, because the "choice"
|
|
||||||
// widget automatically fields invalid values
|
|
||||||
'widget' => 'text',
|
|
||||||
));
|
|
||||||
|
|
||||||
$form->bind(array(
|
|
||||||
'date' => array(
|
|
||||||
'day' => '31',
|
|
||||||
'month' => '9',
|
|
||||||
'year' => '2010',
|
|
||||||
),
|
|
||||||
'time' => array(
|
|
||||||
'hour' => '25',
|
|
||||||
'minute' => '4',
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
$this->assertFalse($form->isValid());
|
|
||||||
$this->assertEquals(array(new FormError('Customized invalid message', array())), $form['date']->getErrors());
|
|
||||||
$this->assertEquals(array(new FormError('Customized invalid message', array())), $form['time']->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bug fix
|
// Bug fix
|
||||||
public function testInitializeWithDateTime()
|
public function testInitializeWithDateTime()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,694 @@
|
||||||
|
<?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\Tests\Extension\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\FormBuilder;
|
||||||
|
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||||
|
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\Util\PropertyPath;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\GlobalExecutionContext;
|
||||||
|
use Symfony\Component\Validator\ExecutionContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||||
|
*/
|
||||||
|
class FormValidatorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $serverParams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var FormValidator
|
||||||
|
*/
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
|
||||||
|
$this->markTestSkipped('The "EventDispatcher" component is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||||
|
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||||
|
$this->serverParams = $this->getMock('Symfony\Component\Form\Extension\Validator\Util\ServerParams');
|
||||||
|
$this->validator = new FormValidator($this->serverParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidate()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', array('group1', 'group2'))
|
||||||
|
->setData($object)
|
||||||
|
->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);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateConstraints()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
$constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', array('group1', 'group2'))
|
||||||
|
->setAttribute('constraints', array($constraint1, $constraint2))
|
||||||
|
->setData($object)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
// First default constraints
|
||||||
|
$graphWalker->expects($this->at(0))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group1', 'data', true);
|
||||||
|
$graphWalker->expects($this->at(1))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group2', 'data', true);
|
||||||
|
|
||||||
|
// Then custom constraints
|
||||||
|
$graphWalker->expects($this->at(2))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint1, $object, 'group1', 'data');
|
||||||
|
$graphWalker->expects($this->at(3))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint1, $object, 'group2', 'data');
|
||||||
|
$graphWalker->expects($this->at(4))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint2, $object, 'group1', 'data');
|
||||||
|
$graphWalker->expects($this->at(5))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint2, $object, 'group2', 'data');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontValidateIfParentWithoutCascadeValidation()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setAttribute('cascade_validation', false)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', array('group1', 'group2'))
|
||||||
|
->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$form->setData($object);
|
||||||
|
|
||||||
|
$graphWalker->expects($this->never())
|
||||||
|
->method('walkReference');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateConstraintsEvenIfNoCascadeValidation()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
$constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setAttribute('cascade_validation', false)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', array('group1', 'group2'))
|
||||||
|
->setAttribute('constraints', array($constraint1, $constraint2))
|
||||||
|
->setData($object)
|
||||||
|
->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$graphWalker->expects($this->at(0))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint1, $object, 'group1', 'data');
|
||||||
|
$graphWalker->expects($this->at(1))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint1, $object, 'group2', 'data');
|
||||||
|
$graphWalker->expects($this->at(2))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint2, $object, 'group1', 'data');
|
||||||
|
$graphWalker->expects($this->at(3))
|
||||||
|
->method('walkConstraint')
|
||||||
|
->with($constraint2, $object, 'group2', 'data');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontValidateIfNotSynchronized()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setData($object)
|
||||||
|
->setAttribute('invalid_message', 'Invalid!')
|
||||||
|
->appendClientTransformer(new CallbackTransformer(
|
||||||
|
function ($data) { return $data; },
|
||||||
|
function () { throw new TransformationFailedException(); }
|
||||||
|
))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
// Launch transformer
|
||||||
|
$form->bind(array());
|
||||||
|
|
||||||
|
$graphWalker->expects($this->never())
|
||||||
|
->method('walkReference');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Invalid!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontValidateConstraintsIfNotSynchronized()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$constraint1 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
$constraint2 = $this->getMock('Symfony\Component\Validator\Constraint');
|
||||||
|
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setData($object)
|
||||||
|
->setAttribute('validation_groups', array('group1', 'group2'))
|
||||||
|
->setAttribute('constraints', array($constraint1, $constraint2))
|
||||||
|
->appendClientTransformer(new CallbackTransformer(
|
||||||
|
function ($data) { return $data; },
|
||||||
|
function () { throw new TransformationFailedException(); }
|
||||||
|
))
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
// Launch transformer
|
||||||
|
$form->bind(array());
|
||||||
|
|
||||||
|
$graphWalker->expects($this->never())
|
||||||
|
->method('walkReference');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleCallbackValidationGroups()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', array($this, 'getValidationGroups'))
|
||||||
|
->setData($object)
|
||||||
|
->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);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHandleClosureValidationGroups()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', function(FormInterface $form){
|
||||||
|
return array('group1', 'group2');
|
||||||
|
})
|
||||||
|
->setData($object)
|
||||||
|
->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);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUseInheritedValidationGroup()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext('foo.bar');
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setAttribute('validation_groups', 'group')
|
||||||
|
->setAttribute('cascade_validation', true)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', null)
|
||||||
|
->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$form->setData($object);
|
||||||
|
|
||||||
|
$graphWalker->expects($this->once())
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group', 'foo.bar.data', true);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUseInheritedCallbackValidationGroup()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext('foo.bar');
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setAttribute('validation_groups', array($this, 'getValidationGroups'))
|
||||||
|
->setAttribute('cascade_validation', true)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', null)
|
||||||
|
->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$form->setData($object);
|
||||||
|
|
||||||
|
$graphWalker->expects($this->at(0))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group1', 'foo.bar.data', true);
|
||||||
|
$graphWalker->expects($this->at(1))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group2', 'foo.bar.data', true);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUseInheritedClosureValidationGroup()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext('foo.bar');
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
|
$parent = $this->getBuilder()
|
||||||
|
->setAttribute('validation_groups', function(FormInterface $form){
|
||||||
|
return array('group1', 'group2');
|
||||||
|
})
|
||||||
|
->setAttribute('cascade_validation', true)
|
||||||
|
->getForm();
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setAttribute('validation_groups', null)
|
||||||
|
->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$form->setData($object);
|
||||||
|
|
||||||
|
$graphWalker->expects($this->at(0))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group1', 'foo.bar.data', true);
|
||||||
|
$graphWalker->expects($this->at(1))
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'group2', 'foo.bar.data', true);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAppendPropertyPath()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext('foo.bar');
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
$object = $this->getMock('\stdClass');
|
||||||
|
$form = $this->getBuilder('name', '\stdClass')
|
||||||
|
->setData($object)
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$graphWalker->expects($this->once())
|
||||||
|
->method('walkReference')
|
||||||
|
->with($object, 'Default', 'foo.bar.data', true);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDontWalkScalars()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$graphWalker = $context->getGraphWalker();
|
||||||
|
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setData('scalar')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$graphWalker->expects($this->never())
|
||||||
|
->method('walkReference');
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViolationIfExtraData()
|
||||||
|
{
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->add($this->getBuilder('child'))
|
||||||
|
->setAttribute('extra_fields_message', 'Extra!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$form->bind(array('foo' => 'bar'));
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Extra!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViolationIfPostMaxSizeExceeded_GigaUpper()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(pow(1024, 3) + 1));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1G'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setAttribute('post_max_size_message', 'Max {{ max }}!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViolationIfPostMaxSizeExceeded_GigaLower()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(pow(1024, 3) + 1));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1g'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setAttribute('post_max_size_message', 'Max {{ max }}!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Max 1G!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfPostMaxSizeNotExceeded_Giga()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(pow(1024, 3)));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1G'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViolationIfPostMaxSizeExceeded_Mega()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(pow(1024, 2) + 1));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1M'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setAttribute('post_max_size_message', 'Max {{ max }}!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Max 1M!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfPostMaxSizeNotExceeded_Mega()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(pow(1024, 2)));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1M'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testViolationIfPostMaxSizeExceeded_Kilo()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1025));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1K'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setAttribute('post_max_size_message', 'Max {{ max }}!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfPostMaxSizeNotExceeded_Kilo()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1024));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1K'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfNotRoot()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1025));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1K'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$parent = $this->getForm();
|
||||||
|
$form = $this->getForm();
|
||||||
|
$parent->add($form);
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfContentLengthNull()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(null));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue('1K'));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTrimPostMaxSize()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1025));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue(' 1K '));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getBuilder()
|
||||||
|
->setAttribute('post_max_size_message', 'Max {{ max }}!')
|
||||||
|
->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(1, $context->getViolations());
|
||||||
|
$this->assertEquals('Max 1K!', $context->getViolations()->get(0)->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfPostMaxSizeEmpty()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1025));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue(' '));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoViolationIfPostMaxSizeNull()
|
||||||
|
{
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getContentLength')
|
||||||
|
->will($this->returnValue(1025));
|
||||||
|
$this->serverParams->expects($this->any())
|
||||||
|
->method('getPostMaxSize')
|
||||||
|
->will($this->returnValue(null));
|
||||||
|
|
||||||
|
$context = $this->getExecutionContext();
|
||||||
|
$form = $this->getForm();
|
||||||
|
|
||||||
|
$this->validator->initialize($context);
|
||||||
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
$this->assertCount(0, $context->getViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMockGraphWalker()
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder('Symfony\Component\Validator\GraphWalker')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMockMetadataFactory()
|
||||||
|
{
|
||||||
|
return $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getExecutionContext($propertyPath = null)
|
||||||
|
{
|
||||||
|
$graphWalker = $this->getMockGraphWalker();
|
||||||
|
$metadataFactory = $this->getMockMetadataFactory();
|
||||||
|
$globalContext = new GlobalExecutionContext('Root', $graphWalker, $metadataFactory);
|
||||||
|
|
||||||
|
return new ExecutionContext($globalContext, null, $propertyPath, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FormBuilder
|
||||||
|
*/
|
||||||
|
private function getBuilder($name = 'name', $dataClass = null)
|
||||||
|
{
|
||||||
|
$builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
|
||||||
|
$builder->setAttribute('constraints', array());
|
||||||
|
|
||||||
|
return $builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getForm($name = 'name', $dataClass = null)
|
||||||
|
{
|
||||||
|
return $this->getBuilder($name, $dataClass)->getForm();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,417 +0,0 @@
|
||||||
<?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\Tests\Extension\Validator\EventListener;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\Event\DataEvent;
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\Form\FormBuilder;
|
|
||||||
use Symfony\Component\Form\FormError;
|
|
||||||
use Symfony\Component\Form\Util\PropertyPath;
|
|
||||||
use Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener;
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
|
||||||
use Symfony\Component\Validator\ConstraintViolation;
|
|
||||||
use Symfony\Component\Validator\GlobalExecutionContext;
|
|
||||||
use Symfony\Component\Validator\ExecutionContext;
|
|
||||||
|
|
||||||
class DelegatingValidationListenerTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $dispatcher;
|
|
||||||
|
|
||||||
private $factory;
|
|
||||||
|
|
||||||
private $builder;
|
|
||||||
|
|
||||||
private $delegate;
|
|
||||||
|
|
||||||
private $listener;
|
|
||||||
|
|
||||||
private $message;
|
|
||||||
|
|
||||||
private $params;
|
|
||||||
|
|
||||||
protected function setUp()
|
|
||||||
{
|
|
||||||
if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
|
|
||||||
$this->markTestSkipped('The "EventDispatcher" component is not available');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
|
||||||
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
|
||||||
$this->delegate = $this->getMock('Symfony\Component\Validator\ValidatorInterface');
|
|
||||||
$this->listener = new DelegatingValidationListener($this->delegate);
|
|
||||||
$this->message = 'Message';
|
|
||||||
$this->params = array('foo' => 'bar');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMockGraphWalker()
|
|
||||||
{
|
|
||||||
return $this->getMockBuilder('Symfony\Component\Validator\GraphWalker')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMockMetadataFactory()
|
|
||||||
{
|
|
||||||
return $this->getMock('Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMockTransformer()
|
|
||||||
{
|
|
||||||
return $this->getMock('Symfony\Component\Form\DataTransformerInterface', array(), array(), '', false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getExecutionContext($propertyPath = null)
|
|
||||||
{
|
|
||||||
$graphWalker = $this->getMockGraphWalker();
|
|
||||||
$metadataFactory = $this->getMockMetadataFactory();
|
|
||||||
$globalContext = new GlobalExecutionContext('Root', $graphWalker, $metadataFactory);
|
|
||||||
|
|
||||||
return new ExecutionContext($globalContext, null, $propertyPath, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getConstraintViolation($propertyPath)
|
|
||||||
{
|
|
||||||
return new ConstraintViolation($this->message, $this->params, null, $propertyPath, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getFormError()
|
|
||||||
{
|
|
||||||
return new FormError($this->message, $this->params);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getBuilder($name = 'name', $propertyPath = null, $dataClass = null)
|
|
||||||
{
|
|
||||||
$builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
|
|
||||||
$builder->setPropertyPath(new PropertyPath($propertyPath ?: $name));
|
|
||||||
$builder->setAttribute('error_mapping', array());
|
|
||||||
$builder->setErrorBubbling(false);
|
|
||||||
$builder->setMapped(true);
|
|
||||||
|
|
||||||
return $builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getForm($name = 'name', $propertyPath = null, $dataClass = null)
|
|
||||||
{
|
|
||||||
return $this->getBuilder($name, $propertyPath, $dataClass)->getForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getMockForm()
|
|
||||||
{
|
|
||||||
return $this->getMock('Symfony\Component\Form\Tests\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');
|
|
||||||
$form = $this
|
|
||||||
->getBuilder('name')
|
|
||||||
->setAttribute('validation_constraint', $constraint)
|
|
||||||
->getForm();
|
|
||||||
|
|
||||||
$this->delegate->expects($this->once())->method('validateValue');
|
|
||||||
|
|
||||||
$this->listener->validateForm(new DataEvent($form, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
// More specific mapping tests can be found in ViolationMapperTest
|
|
||||||
public function testFormErrorMapping()
|
|
||||||
{
|
|
||||||
$parent = $this->getForm();
|
|
||||||
$child = $this->getForm('street');
|
|
||||||
|
|
||||||
$parent->add($child);
|
|
||||||
|
|
||||||
$this->delegate->expects($this->once())
|
|
||||||
->method('validate')
|
|
||||||
->will($this->returnValue(array(
|
|
||||||
$this->getConstraintViolation('children[street].data.constrainedProp')
|
|
||||||
)));
|
|
||||||
|
|
||||||
$this->listener->validateForm(new DataEvent($parent, null));
|
|
||||||
|
|
||||||
$this->assertFalse($parent->hasErrors());
|
|
||||||
$this->assertEquals(array($this->getFormError()), $child->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
// More specific mapping tests can be found in ViolationMapperTest
|
|
||||||
public function testDataErrorMapping()
|
|
||||||
{
|
|
||||||
$parent = $this->getForm();
|
|
||||||
$child = $this->getForm('firstName');
|
|
||||||
|
|
||||||
$parent->add($child);
|
|
||||||
|
|
||||||
$this->delegate->expects($this->once())
|
|
||||||
->method('validate')
|
|
||||||
->will($this->returnValue(array(
|
|
||||||
$this->getConstraintViolation('data.firstName.constrainedProp')
|
|
||||||
)));
|
|
||||||
|
|
||||||
$this->listener->validateForm(new DataEvent($parent, null));
|
|
||||||
|
|
||||||
$this->assertFalse($parent->hasErrors());
|
|
||||||
$this->assertEquals(array($this->getFormError()), $child->getErrors());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormData()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
$form = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->setAttribute('validation_groups', 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);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataCanHandleCallbackValidationGroups()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
$form = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->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);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataCanHandleClosureValidationGroups()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
$form = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->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);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataUsesInheritedValidationGroup()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext('foo.bar');
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
|
|
||||||
$parent = $this->getBuilder()
|
|
||||||
->setAttribute('validation_groups', 'group')
|
|
||||||
->getForm();
|
|
||||||
$child = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->setAttribute('validation_groups', null)
|
|
||||||
->getForm();
|
|
||||||
$parent->add($child);
|
|
||||||
|
|
||||||
$child->setData($object);
|
|
||||||
|
|
||||||
$graphWalker->expects($this->once())
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'group', 'foo.bar.data', true);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($child, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataUsesInheritedCallbackValidationGroup()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext('foo.bar');
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
|
|
||||||
$parent = $this->getBuilder()
|
|
||||||
->setAttribute('validation_groups', array($this, 'getValidationGroups'))
|
|
||||||
->getForm();
|
|
||||||
$child = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->setAttribute('validation_groups', null)
|
|
||||||
->getForm();
|
|
||||||
$parent->add($child);
|
|
||||||
|
|
||||||
$child->setData($object);
|
|
||||||
|
|
||||||
$graphWalker->expects($this->at(0))
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'group1', 'foo.bar.data', true);
|
|
||||||
$graphWalker->expects($this->at(1))
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'group2', 'foo.bar.data', true);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($child, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataUsesInheritedClosureValidationGroup()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext('foo.bar');
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
|
|
||||||
$parent = $this->getBuilder()
|
|
||||||
->setAttribute('validation_groups', function(FormInterface $form){
|
|
||||||
return array('group1', 'group2');
|
|
||||||
})
|
|
||||||
->getForm();
|
|
||||||
$child = $this->getBuilder('name', null, '\stdClass')
|
|
||||||
->setAttribute('validation_groups', null)
|
|
||||||
->getForm();
|
|
||||||
$parent->add($child);
|
|
||||||
|
|
||||||
$child->setData($object);
|
|
||||||
|
|
||||||
$graphWalker->expects($this->at(0))
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'group1', 'foo.bar.data', true);
|
|
||||||
$graphWalker->expects($this->at(1))
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'group2', 'foo.bar.data', true);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($child, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataAppendsPropertyPath()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext('foo.bar');
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$object = $this->getMock('\stdClass');
|
|
||||||
$form = $this->getForm('name', null, '\stdClass');
|
|
||||||
|
|
||||||
$graphWalker->expects($this->once())
|
|
||||||
->method('walkReference')
|
|
||||||
->with($object, 'Default', 'foo.bar.data', true);
|
|
||||||
|
|
||||||
$form->setData($object);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormDataDoesNotWalkScalars()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$clientTransformer = $this->getMockTransformer();
|
|
||||||
|
|
||||||
$form = $this->getBuilder()
|
|
||||||
->appendClientTransformer($clientTransformer)
|
|
||||||
->getForm();
|
|
||||||
|
|
||||||
$graphWalker->expects($this->never())
|
|
||||||
->method('walkReference');
|
|
||||||
|
|
||||||
$clientTransformer->expects($this->atLeastOnce())
|
|
||||||
->method('reverseTransform')
|
|
||||||
->will($this->returnValue('foobar'));
|
|
||||||
|
|
||||||
$form->bind(array('foo' => 'bar')); // reverse transformed to "foobar"
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormData($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormChildren()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$form = $this->getBuilder()
|
|
||||||
->setAttribute('cascade_validation', true)
|
|
||||||
->setAttribute('validation_groups', array('group1', 'group2'))
|
|
||||||
->getForm();
|
|
||||||
$form->add($this->getForm('firstName'));
|
|
||||||
|
|
||||||
$graphWalker->expects($this->once())
|
|
||||||
->method('walkReference')
|
|
||||||
// validation happens in Default group, because the Callback
|
|
||||||
// constraint is in the Default group as well
|
|
||||||
->with($form->getChildren(), Constraint::DEFAULT_GROUP, 'children', true);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormChildren($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormChildrenAppendsPropertyPath()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext('foo.bar');
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$form = $this->getBuilder()
|
|
||||||
->setAttribute('cascade_validation', true)
|
|
||||||
->getForm();
|
|
||||||
$form->add($this->getForm('firstName'));
|
|
||||||
|
|
||||||
$graphWalker->expects($this->once())
|
|
||||||
->method('walkReference')
|
|
||||||
->with($form->getChildren(), 'Default', 'foo.bar.children', true);
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormChildren($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateFormChildrenDoesNothingIfDisabled()
|
|
||||||
{
|
|
||||||
$context = $this->getExecutionContext();
|
|
||||||
$graphWalker = $context->getGraphWalker();
|
|
||||||
$form = $this->getBuilder()
|
|
||||||
->setAttribute('cascade_validation', false)
|
|
||||||
->getForm();
|
|
||||||
$form->add($this->getForm('firstName'));
|
|
||||||
|
|
||||||
$graphWalker->expects($this->never())
|
|
||||||
->method('walkReference');
|
|
||||||
|
|
||||||
DelegatingValidationListener::validateFormChildren($form, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testValidateIgnoresNonRoot()
|
|
||||||
{
|
|
||||||
$form = $this->getMockForm();
|
|
||||||
$form->expects($this->once())
|
|
||||||
->method('isRoot')
|
|
||||||
->will($this->returnValue(false));
|
|
||||||
|
|
||||||
$this->delegate->expects($this->never())
|
|
||||||
->method('validate');
|
|
||||||
|
|
||||||
$this->listener->validateForm(new DataEvent($form, null));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
<?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\Tests\Extension\Validator\EventListener;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\Event\DataEvent;
|
||||||
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\Form\FormBuilder;
|
||||||
|
use Symfony\Component\Form\FormError;
|
||||||
|
use Symfony\Component\Form\Util\PropertyPath;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||||
|
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintViolation;
|
||||||
|
use Symfony\Component\Validator\GlobalExecutionContext;
|
||||||
|
use Symfony\Component\Validator\ExecutionContext;
|
||||||
|
|
||||||
|
class ValidationListenerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $dispatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $validator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \PHPUnit_Framework_MockObject_MockObject
|
||||||
|
*/
|
||||||
|
private $violationMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ValidationListener
|
||||||
|
*/
|
||||||
|
private $listener;
|
||||||
|
|
||||||
|
private $message;
|
||||||
|
|
||||||
|
private $params;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
|
||||||
|
$this->markTestSkipped('The "EventDispatcher" component is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||||
|
$this->factory = $this->getMock('Symfony\Component\Form\FormFactoryInterface');
|
||||||
|
$this->validator = $this->getMock('Symfony\Component\Validator\ValidatorInterface');
|
||||||
|
$this->violationMapper = $this->getMock('Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationMapperInterface');
|
||||||
|
$this->listener = new ValidationListener($this->validator, $this->violationMapper);
|
||||||
|
$this->message = 'Message';
|
||||||
|
$this->params = array('foo' => 'bar');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getConstraintViolation($code = null)
|
||||||
|
{
|
||||||
|
return new ConstraintViolation($this->message, $this->params, null, 'prop.path', null, null, $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFormError()
|
||||||
|
{
|
||||||
|
return new FormError($this->message, $this->params);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBuilder($name = 'name', $propertyPath = null, $dataClass = null)
|
||||||
|
{
|
||||||
|
$builder = new FormBuilder($name, $dataClass, $this->dispatcher, $this->factory);
|
||||||
|
$builder->setPropertyPath(new PropertyPath($propertyPath ?: $name));
|
||||||
|
$builder->setAttribute('error_mapping', array());
|
||||||
|
$builder->setErrorBubbling(false);
|
||||||
|
$builder->setMapped(true);
|
||||||
|
|
||||||
|
return $builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getForm($name = 'name', $propertyPath = null, $dataClass = null)
|
||||||
|
{
|
||||||
|
return $this->getBuilder($name, $propertyPath, $dataClass)->getForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMockForm()
|
||||||
|
{
|
||||||
|
return $this->getMock('Symfony\Component\Form\Tests\FormInterface');
|
||||||
|
}
|
||||||
|
|
||||||
|
// More specific mapping tests can be found in ViolationMapperTest
|
||||||
|
public function testMapViolation()
|
||||||
|
{
|
||||||
|
$violation = $this->getConstraintViolation();
|
||||||
|
$form = $this->getForm('street');
|
||||||
|
|
||||||
|
$this->validator->expects($this->once())
|
||||||
|
->method('validate')
|
||||||
|
->will($this->returnValue(array($violation)));
|
||||||
|
|
||||||
|
$this->violationMapper->expects($this->once())
|
||||||
|
->method('mapViolation')
|
||||||
|
->with($violation, $form, false);
|
||||||
|
|
||||||
|
$this->listener->validateForm(new DataEvent($form, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMapViolationAllowsNonSyncIfInvalid()
|
||||||
|
{
|
||||||
|
$violation = $this->getConstraintViolation(Form::ERR_INVALID);
|
||||||
|
$form = $this->getForm('street');
|
||||||
|
|
||||||
|
$this->validator->expects($this->once())
|
||||||
|
->method('validate')
|
||||||
|
->will($this->returnValue(array($violation)));
|
||||||
|
|
||||||
|
$this->violationMapper->expects($this->once())
|
||||||
|
->method('mapViolation')
|
||||||
|
// pass true now
|
||||||
|
->with($violation, $form, true);
|
||||||
|
|
||||||
|
$this->listener->validateForm(new DataEvent($form, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testValidateIgnoresNonRoot()
|
||||||
|
{
|
||||||
|
$form = $this->getMockForm();
|
||||||
|
$form->expects($this->once())
|
||||||
|
->method('isRoot')
|
||||||
|
->will($this->returnValue(false));
|
||||||
|
|
||||||
|
$this->validator->expects($this->never())
|
||||||
|
->method('validate');
|
||||||
|
|
||||||
|
$this->violationMapper->expects($this->never())
|
||||||
|
->method('mapViolation');
|
||||||
|
|
||||||
|
$this->listener->validateForm(new DataEvent($form, null));
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,8 +24,9 @@ class ConstraintViolation
|
||||||
protected $root;
|
protected $root;
|
||||||
protected $propertyPath;
|
protected $propertyPath;
|
||||||
protected $invalidValue;
|
protected $invalidValue;
|
||||||
|
protected $code;
|
||||||
|
|
||||||
public function __construct($messageTemplate, array $messageParameters, $root, $propertyPath, $invalidValue, $messagePluralization = null)
|
public function __construct($messageTemplate, array $messageParameters, $root, $propertyPath, $invalidValue, $messagePluralization = null, $code = null)
|
||||||
{
|
{
|
||||||
$this->messageTemplate = $messageTemplate;
|
$this->messageTemplate = $messageTemplate;
|
||||||
$this->messageParameters = $messageParameters;
|
$this->messageParameters = $messageParameters;
|
||||||
|
@ -33,6 +34,7 @@ class ConstraintViolation
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
$this->propertyPath = $propertyPath;
|
$this->propertyPath = $propertyPath;
|
||||||
$this->invalidValue = $invalidValue;
|
$this->invalidValue = $invalidValue;
|
||||||
|
$this->code = $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,12 +44,17 @@ class ConstraintViolation
|
||||||
{
|
{
|
||||||
$class = (string) (is_object($this->root) ? get_class($this->root) : $this->root);
|
$class = (string) (is_object($this->root) ? get_class($this->root) : $this->root);
|
||||||
$propertyPath = (string) $this->propertyPath;
|
$propertyPath = (string) $this->propertyPath;
|
||||||
|
$code = $this->code;
|
||||||
|
|
||||||
if ('' !== $propertyPath && '[' !== $propertyPath[0] && '' !== $class) {
|
if ('' !== $propertyPath && '[' !== $propertyPath[0] && '' !== $class) {
|
||||||
$class .= '.';
|
$class .= '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $class . $propertyPath . ":\n " . $this->getMessage();
|
if (!empty($code)) {
|
||||||
|
$code = ' (code ' . $code . ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $class . $propertyPath . ":\n " . $this->getMessage() . $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -112,4 +119,9 @@ class ConstraintViolation
|
||||||
{
|
{
|
||||||
return $this->invalidValue;
|
return $this->invalidValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCode()
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,57 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the violation at a given offset.
|
||||||
|
*
|
||||||
|
* @param integer $offset The offset of the violation.
|
||||||
|
*
|
||||||
|
* @return ConstraintViolation The violation.
|
||||||
|
*
|
||||||
|
* @throws \OutOfBoundsException If the offset does not exist.
|
||||||
|
*/
|
||||||
|
public function get($offset)
|
||||||
|
{
|
||||||
|
if (!isset($this->violations[$offset])) {
|
||||||
|
throw new \OutOfBoundsException(sprintf('The offset "%s" does not exist.', $offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->violations[$offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the given offset exists.
|
||||||
|
*
|
||||||
|
* @param integer $offset The violation offset.
|
||||||
|
*
|
||||||
|
* @return Boolean Whether the offset exists.
|
||||||
|
*/
|
||||||
|
public function has($offset)
|
||||||
|
{
|
||||||
|
return isset($this->violations[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a violation at a given offset.
|
||||||
|
*
|
||||||
|
* @param integer $offset The violation offset.
|
||||||
|
* @param ConstraintViolation $violation The violation.
|
||||||
|
*/
|
||||||
|
public function set($offset, ConstraintViolation $violation)
|
||||||
|
{
|
||||||
|
$this->violations[$offset] = $violation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a violation at a given offset.
|
||||||
|
*
|
||||||
|
* @param integer $offset The offset to remove.
|
||||||
|
*/
|
||||||
|
public function remove($offset)
|
||||||
|
{
|
||||||
|
unset($this->violations[$offset]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see IteratorAggregate
|
* @see IteratorAggregate
|
||||||
*
|
*
|
||||||
|
@ -106,7 +157,7 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
||||||
*/
|
*/
|
||||||
public function offsetExists($offset)
|
public function offsetExists($offset)
|
||||||
{
|
{
|
||||||
return isset($this->violations[$offset]);
|
return $this->has($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +167,7 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
||||||
*/
|
*/
|
||||||
public function offsetGet($offset)
|
public function offsetGet($offset)
|
||||||
{
|
{
|
||||||
return isset($this->violations[$offset]) ? $this->violations[$offset] : null;
|
return $this->get($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,12 +175,12 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function offsetSet($offset, $value)
|
public function offsetSet($offset, $violation)
|
||||||
{
|
{
|
||||||
if (null === $offset) {
|
if (null === $offset) {
|
||||||
$this->violations[] = $value;
|
$this->add($violation);
|
||||||
} else {
|
} else {
|
||||||
$this->violations[$offset] = $value;
|
$this->set($offset, $violation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +191,7 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
||||||
*/
|
*/
|
||||||
public function offsetUnset($offset)
|
public function offsetUnset($offset)
|
||||||
{
|
{
|
||||||
unset($this->violations[$offset]);
|
$this->remove($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,10 +57,11 @@ class ExecutionContext
|
||||||
* @param array $params The parameters parsed into the error message.
|
* @param array $params The parameters parsed into the error message.
|
||||||
* @param mixed $invalidValue The invalid, validated value.
|
* @param mixed $invalidValue The invalid, validated value.
|
||||||
* @param integer|null $pluralization The number to use to pluralize of the message.
|
* @param integer|null $pluralization The number to use to pluralize of the message.
|
||||||
|
* @param integer|null $code The violation code.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function addViolation($message, array $params = array(), $invalidValue = null, $pluralization = null)
|
public function addViolation($message, array $params = array(), $invalidValue = null, $pluralization = null, $code = null)
|
||||||
{
|
{
|
||||||
$this->globalContext->addViolation(new ConstraintViolation(
|
$this->globalContext->addViolation(new ConstraintViolation(
|
||||||
$message,
|
$message,
|
||||||
|
@ -69,7 +70,8 @@ class ExecutionContext
|
||||||
$this->propertyPath,
|
$this->propertyPath,
|
||||||
// check using func_num_args() to allow passing null values
|
// check using func_num_args() to allow passing null values
|
||||||
func_num_args() >= 3 ? $invalidValue : $this->value,
|
func_num_args() >= 3 ? $invalidValue : $this->value,
|
||||||
$pluralization
|
$pluralization,
|
||||||
|
$code
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +84,9 @@ class ExecutionContext
|
||||||
* @param array $params The parameters parsed into the error message.
|
* @param array $params The parameters parsed into the error message.
|
||||||
* @param mixed $invalidValue The invalid, validated value.
|
* @param mixed $invalidValue The invalid, validated value.
|
||||||
* @param integer|null $pluralization The number to use to pluralize of the message.
|
* @param integer|null $pluralization The number to use to pluralize of the message.
|
||||||
|
* @param integer|null $code The violation code.
|
||||||
*/
|
*/
|
||||||
public function addViolationAtPath($propertyPath, $message, array $params = array(), $invalidValue = null, $pluralization = null)
|
public function addViolationAtPath($propertyPath, $message, array $params = array(), $invalidValue = null, $pluralization = null, $code = null)
|
||||||
{
|
{
|
||||||
$this->globalContext->addViolation(new ConstraintViolation(
|
$this->globalContext->addViolation(new ConstraintViolation(
|
||||||
$message,
|
$message,
|
||||||
|
@ -92,7 +95,8 @@ class ExecutionContext
|
||||||
$propertyPath,
|
$propertyPath,
|
||||||
// check using func_num_args() to allow passing null values
|
// check using func_num_args() to allow passing null values
|
||||||
func_num_args() >= 4 ? $invalidValue : $this->value,
|
func_num_args() >= 4 ? $invalidValue : $this->value,
|
||||||
$pluralization
|
$pluralization,
|
||||||
|
$code
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +109,9 @@ class ExecutionContext
|
||||||
* @param array $params The parameters parsed into the error message.
|
* @param array $params The parameters parsed into the error message.
|
||||||
* @param mixed $invalidValue The invalid, validated value.
|
* @param mixed $invalidValue The invalid, validated value.
|
||||||
* @param integer|null $pluralization The number to use to pluralize of the message.
|
* @param integer|null $pluralization The number to use to pluralize of the message.
|
||||||
|
* @param integer|null $code The violation code.
|
||||||
*/
|
*/
|
||||||
public function addViolationAtSubPath($subPath, $message, array $params = array(), $invalidValue = null, $pluralization = null)
|
public function addViolationAtSubPath($subPath, $message, array $params = array(), $invalidValue = null, $pluralization = null, $code = null)
|
||||||
{
|
{
|
||||||
$this->globalContext->addViolation(new ConstraintViolation(
|
$this->globalContext->addViolation(new ConstraintViolation(
|
||||||
$message,
|
$message,
|
||||||
|
@ -115,7 +120,8 @@ class ExecutionContext
|
||||||
$this->getPropertyPath($subPath),
|
$this->getPropertyPath($subPath),
|
||||||
// check using func_num_args() to allow passing null values
|
// check using func_num_args() to allow passing null values
|
||||||
func_num_args() >= 4 ? $invalidValue : $this->value,
|
func_num_args() >= 4 ? $invalidValue : $this->value,
|
||||||
$pluralization
|
$pluralization,
|
||||||
|
$code
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue