[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();
|
||||
```
|
||||
|
||||
* 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
|
||||
|
||||
* The methods `setMessage()`, `getMessageTemplate()` and
|
||||
|
|
|
@ -133,9 +133,12 @@
|
|||
</service>
|
||||
|
||||
<!-- 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" />
|
||||
<argument type="service" id="validator" />
|
||||
</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>
|
||||
</container>
|
||||
|
|
|
@ -83,3 +83,5 @@ CHANGELOG
|
|||
* `getErrorBubbling`
|
||||
* `getNormTransformers`
|
||||
* `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',
|
||||
'empty_value',
|
||||
'required',
|
||||
'invalid_message',
|
||||
'invalid_message_parameters',
|
||||
'translation_domain',
|
||||
)));
|
||||
$timeOptions = array_intersect_key($options, array_flip(array(
|
||||
|
@ -62,8 +60,6 @@ class DateTimeType extends AbstractType
|
|||
'with_seconds',
|
||||
'empty_value',
|
||||
'required',
|
||||
'invalid_message',
|
||||
'invalid_message_parameters',
|
||||
'translation_domain',
|
||||
)));
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ use Symfony\Component\Form\FormInterface;
|
|||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
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\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
|
@ -50,19 +49,15 @@ class FormType extends AbstractType
|
|||
->setVirtual($options['virtual'])
|
||||
->setAttribute('read_only', $options['read_only'])
|
||||
->setAttribute('by_reference', $options['by_reference'])
|
||||
->setAttribute('error_mapping', $options['error_mapping'])
|
||||
->setAttribute('max_length', $options['max_length'])
|
||||
->setAttribute('pattern', $options['pattern'])
|
||||
->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName()))
|
||||
->setAttribute('attr', $options['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('single_control', $options['single_control'])
|
||||
->setData($options['data'])
|
||||
->setDataMapper(new PropertyPathMapper())
|
||||
->addEventSubscriber(new ValidationListener())
|
||||
;
|
||||
|
||||
if ($options['trim']) {
|
||||
|
@ -210,14 +205,11 @@ class FormType extends AbstractType
|
|||
'mapped' => $mapped,
|
||||
'by_reference' => true,
|
||||
'error_bubbling' => $errorBubbling,
|
||||
'error_mapping' => array(),
|
||||
'label' => null,
|
||||
'attr' => array(),
|
||||
'label_attr' => array(),
|
||||
'virtual' => false,
|
||||
'single_control' => false,
|
||||
'invalid_message' => 'This value is not valid.',
|
||||
'invalid_message_parameters' => array(),
|
||||
'translation_domain' => 'messages',
|
||||
);
|
||||
}
|
||||
|
|
|
@ -42,11 +42,6 @@ class RepeatedType extends AbstractType
|
|||
*/
|
||||
public function getDefaultOptions()
|
||||
{
|
||||
// Map errors to the first field
|
||||
$errorMapping = function (Options $options) {
|
||||
return array('.' => $options['first_name']);
|
||||
};
|
||||
|
||||
return array(
|
||||
'type' => 'text',
|
||||
'options' => array(),
|
||||
|
@ -55,7 +50,6 @@ class RepeatedType extends AbstractType
|
|||
'first_name' => 'first',
|
||||
'second_name' => 'second',
|
||||
'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\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\OptionsResolver\Options;
|
||||
|
||||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class FormTypeValidatorExtension extends AbstractTypeExtension
|
||||
{
|
||||
/**
|
||||
* @var ValidatorInterface
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
/**
|
||||
* @var ViolationMapper
|
||||
*/
|
||||
private $violationMapper;
|
||||
|
||||
public function __construct(ValidatorInterface $validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
$this->violationMapper = new ViolationMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(FormBuilder $builder, array $options)
|
||||
{
|
||||
if (empty($options['validation_groups'])) {
|
||||
|
@ -38,23 +52,49 @@ class FormTypeValidatorExtension extends AbstractTypeExtension
|
|||
: (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
|
||||
->setAttribute('error_mapping', $options['error_mapping'])
|
||||
->setAttribute('validation_groups', $options['validation_groups'])
|
||||
->setAttribute('validation_constraint', $options['validation_constraint'])
|
||||
->setAttribute('constraints', $constraints)
|
||||
->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()
|
||||
{
|
||||
// BC clause
|
||||
$constraints = function (Options $options) {
|
||||
return $options['validation_constraint'];
|
||||
};
|
||||
|
||||
return array(
|
||||
'error_mapping' => array(),
|
||||
'validation_groups' => null,
|
||||
// "validation_constraint" is deprecated. Use "constraints".
|
||||
'validation_constraint' => null,
|
||||
'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()
|
||||
{
|
||||
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;
|
||||
|
||||
use Symfony\Component\Form\Extension\Validator\Type;
|
||||
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
use Symfony\Component\Validator\ValidatorInterface;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\Valid;
|
||||
|
||||
class ValidatorExtension extends AbstractExtension
|
||||
|
@ -26,7 +26,7 @@ class ValidatorExtension extends AbstractExtension
|
|||
$this->validator = $validator;
|
||||
|
||||
$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());
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ class ValidatorExtension extends AbstractExtension
|
|||
{
|
||||
return array(
|
||||
new Type\FormTypeValidatorExtension($this->validator),
|
||||
new Type\RepeatedTypeValidatorExtension(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use Symfony\Component\Validator\ConstraintViolation;
|
|||
/**
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
class ViolationMapper
|
||||
class ViolationMapper implements ViolationMapperInterface
|
||||
{
|
||||
/**
|
||||
* @var FormInterface
|
||||
|
@ -41,15 +41,17 @@ class ViolationMapper
|
|||
private $rules = array();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @var Boolean
|
||||
*/
|
||||
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());
|
||||
$relativePath = $this->reconstructPath($violationPath, $form);
|
||||
$match = false;
|
||||
|
@ -74,7 +76,7 @@ class ViolationMapper
|
|||
$this->setScope($relativePath->getRoot());
|
||||
$it = new PropertyPathIterator($relativePath);
|
||||
|
||||
while ($this->scope->isSynchronized() && null !== ($child = $this->matchChild($it))) {
|
||||
while ($this->isValidScope() && null !== ($child = $this->matchChild($it))) {
|
||||
$this->setScope($child);
|
||||
$it->next();
|
||||
$match = true;
|
||||
|
@ -95,7 +97,7 @@ class ViolationMapper
|
|||
// The overhead of setScope() is not needed anymore here
|
||||
$this->scope = $form;
|
||||
|
||||
while ($this->scope->isSynchronized() && $it->valid() && $it->mapsForm()) {
|
||||
while ($this->isValidScope() && $it->valid() && $it->mapsForm()) {
|
||||
if (!$this->scope->has($it->current())) {
|
||||
// Break if we find a reference to a non-existing child
|
||||
break;
|
||||
|
@ -109,14 +111,14 @@ class ViolationMapper
|
|||
// Follow dot rules until we have the final target
|
||||
$mapping = $this->scope->getAttribute('error_mapping');
|
||||
|
||||
while ($this->scope->isSynchronized() && isset($mapping['.'])) {
|
||||
while ($this->isValidScope() && isset($mapping['.'])) {
|
||||
$dotRule = new MappingRule($this->scope, '.', $mapping['.']);
|
||||
$this->scope = $dotRule->getTarget();
|
||||
$mapping = $this->scope->getAttribute('error_mapping');
|
||||
}
|
||||
|
||||
// Only add the error if the form is synchronized
|
||||
if ($this->scope->isSynchronized()) {
|
||||
if ($this->isValidScope()) {
|
||||
$this->scope->addError(new FormError(
|
||||
$violation->getMessageTemplate(),
|
||||
$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">
|
||||
|
||||
<class name="Symfony\Component\Form\Form">
|
||||
<constraint name="Callback">
|
||||
<value>
|
||||
<value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
|
||||
<value>validateFormData</value>
|
||||
</value>
|
||||
<value>
|
||||
<value>Symfony\Component\Form\Extension\Validator\EventListener\DelegatingValidationListener</value>
|
||||
<value>validateFormChildren</value>
|
||||
</value>
|
||||
</constraint>
|
||||
<constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
|
||||
<property name="children">
|
||||
<constraint name="Valid" />
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
@ -226,32 +226,6 @@ class DateTimeTypeTest extends LocalizedTestCase
|
|||
$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
|
||||
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 $propertyPath;
|
||||
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->messageParameters = $messageParameters;
|
||||
|
@ -33,6 +34,7 @@ class ConstraintViolation
|
|||
$this->root = $root;
|
||||
$this->propertyPath = $propertyPath;
|
||||
$this->invalidValue = $invalidValue;
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,12 +44,17 @@ class ConstraintViolation
|
|||
{
|
||||
$class = (string) (is_object($this->root) ? get_class($this->root) : $this->root);
|
||||
$propertyPath = (string) $this->propertyPath;
|
||||
$code = $this->code;
|
||||
|
||||
if ('' !== $propertyPath && '[' !== $propertyPath[0] && '' !== $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;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
|
@ -106,7 +157,7 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
|||
*/
|
||||
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)
|
||||
{
|
||||
return isset($this->violations[$offset]) ? $this->violations[$offset] : null;
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,12 +175,12 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
|||
*
|
||||
* @api
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
public function offsetSet($offset, $violation)
|
||||
{
|
||||
if (null === $offset) {
|
||||
$this->violations[] = $value;
|
||||
$this->add($violation);
|
||||
} else {
|
||||
$this->violations[$offset] = $value;
|
||||
$this->set($offset, $violation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +191,7 @@ class ConstraintViolationList implements \IteratorAggregate, \Countable, \ArrayA
|
|||
*/
|
||||
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 mixed $invalidValue The invalid, validated value.
|
||||
* @param integer|null $pluralization The number to use to pluralize of the message.
|
||||
* @param integer|null $code The violation code.
|
||||
*
|
||||
* @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(
|
||||
$message,
|
||||
|
@ -69,7 +70,8 @@ class ExecutionContext
|
|||
$this->propertyPath,
|
||||
// check using func_num_args() to allow passing null values
|
||||
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 mixed $invalidValue The invalid, validated value.
|
||||
* @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(
|
||||
$message,
|
||||
|
@ -92,7 +95,8 @@ class ExecutionContext
|
|||
$propertyPath,
|
||||
// check using func_num_args() to allow passing null values
|
||||
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 mixed $invalidValue The invalid, validated value.
|
||||
* @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(
|
||||
$message,
|
||||
|
@ -115,7 +120,8 @@ class ExecutionContext
|
|||
$this->getPropertyPath($subPath),
|
||||
// check using func_num_args() to allow passing null values
|
||||
func_num_args() >= 4 ? $invalidValue : $this->value,
|
||||
$pluralization
|
||||
$pluralization,
|
||||
$code
|
||||
));
|
||||
}
|
||||
|
||||
|
|
Reference in New Issue