[Form] The form is now validated seperatedly from its data. The form is validated in group "Default", the data in the group set in option "validation_groups"

This commit is contained in:
Bernhard Schussek 2011-02-02 15:10:06 +01:00
parent c923af2879
commit 5e3fab214e
8 changed files with 146 additions and 88 deletions

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form;
/**
* Wraps errors in the form data
*
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
*/
class DataError extends Error
{
}

View File

@ -0,0 +1,65 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form;
/**
* Wraps errors in forms
*
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
*/
class Error
{
/**
* The template for the error message
* @var string
*/
protected $messageTemplate;
/**
* The parameters that should be substituted in the message template
* @var array
*/
protected $messageParameters;
/**
* Constructor
*
* @param string $messageTemplate The template for the error message
* @param array $messageParameters The parameters that should be
* substituted in the message template.
*/
public function __construct($messageTemplate, array $messageParameters = array())
{
$this->messageTemplate = $messageTemplate;
$this->messageParameters = $messageParameters;
}
/**
* Returns the error message template
*
* @return string
*/
public function getMessageTemplate()
{
return $this->messageTemplate;
}
/**
* Returns the parameters to be inserted in the message template
*
* @return array
*/
public function getMessageParameters()
{
return $this->messageParameters;
}
}

View File

@ -358,7 +358,7 @@ class Field extends Configurable implements FieldInterface
*
* @see FieldInterface
*/
public function addError(FieldError $error, PropertyPathIterator $pathIterator = null, $type = null)
public function addError(Error $error, PropertyPathIterator $pathIterator = null)
{
$this->errors[] = $error;
}

View File

@ -16,42 +16,6 @@ namespace Symfony\Component\Form;
*
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
*/
class FieldError
class FieldError extends Error
{
protected $messageTemplate;
protected $messageParameters;
/**
* Constructor
*
* @param string $messageTemplate The template for the error message
* @param array $messageParameters The parameters that should be
* substituted in the message template.
*/
public function __construct($messageTemplate, array $messageParameters = array())
{
$this->messageTemplate = $messageTemplate;
$this->messageParameters = $messageParameters;
}
/**
* Returns the error message template
*
* @return string
*/
public function getMessageTemplate()
{
return $this->messageTemplate;
}
/**
* Returns the parameters to be inserted in the message template
*
* @return array
*/
public function getMessageParameters()
{
return $this->messageParameters;
}
}

View File

@ -154,11 +154,10 @@ interface FieldInterface
* ...
* </code>
*
* @param FieldError $error
* @param Error $error
* @param PropertyPathIterator $pathIterator
* @param ConstraintViolation$violation
*/
function addError(FieldError $error, PropertyPathIterator $pathIterator = null, $type = null);
function addError(Error $error, PropertyPathIterator $pathIterator = null);
/**
* Returns whether the field is valid.

View File

@ -516,10 +516,10 @@ class Form extends Field implements \IteratorAggregate, FormInterface
/**
* {@inheritDoc}
*/
public function addError(FieldError $error, PropertyPathIterator $pathIterator = null, $type = null)
public function addError(Error $error, PropertyPathIterator $pathIterator = null)
{
if (null !== $pathIterator) {
if ($type === self::FIELD_ERROR && $pathIterator->hasNext()) {
if ($error instanceof FieldError && $pathIterator->hasNext()) {
$pathIterator->next();
if ($pathIterator->isProperty() && $pathIterator->current() === 'fields') {
@ -527,11 +527,11 @@ class Form extends Field implements \IteratorAggregate, FormInterface
}
if ($this->has($pathIterator->current()) && !$this->get($pathIterator->current())->isHidden()) {
$this->get($pathIterator->current())->addError($error, $pathIterator, $type);
$this->get($pathIterator->current())->addError($error, $pathIterator);
return;
}
} else if ($type === self::DATA_ERROR) {
} else if ($error instanceof DataError) {
$iterator = new RecursiveFieldIterator($this);
$iterator = new \RecursiveIteratorIterator($iterator);
@ -542,7 +542,7 @@ class Form extends Field implements \IteratorAggregate, FormInterface
$pathIterator->next();
}
$field->addError($error, $pathIterator, $type);
$field->addError($error, $pathIterator);
return;
}
@ -729,25 +729,34 @@ class Form extends Field implements \IteratorAggregate, FormInterface
*/
public function validate()
{
if (null === $this->getOption('validator')) {
$validator = $this->getOption('validator');
$groups = $this->getOption('validation_groups');
if (null === $validator) {
throw new MissingOptionsException('The option "validator" is required for validating', array('validator'));
}
// Validate the submitted data
if ($violations = $this->getOption('validator')->validate($this, $this->getOption('validation_groups'))) {
// TODO: test me
// Validate the submitted data in the domain object in the sets
// validation group(s)
if ($violations = $validator->validate($this->getData(), $groups)) {
foreach ($violations as $violation) {
$propertyPath = new PropertyPath($violation->getPropertyPath());
$iterator = $propertyPath->getIterator();
$iterator->next(); // point at the first data element
$error = new DataError($violation->getMessageTemplate(), $violation->getMessageParameters());
if ($iterator->current() == 'data') {
$type = self::DATA_ERROR;
$iterator->next(); // point at the first data element
} else {
$type = self::FIELD_ERROR;
}
$this->addError($error, $iterator);
}
}
$this->addError(new FieldError($violation->getMessageTemplate(), $violation->getMessageParameters()), $iterator, $type);
// Validate the submitted data in the fields in group "Default"
if ($violations = $validator->validate($this)) {
foreach ($violations as $violation) {
$propertyPath = new PropertyPath($violation->getPropertyPath());
$iterator = $propertyPath->getIterator();
$error = new FieldError($violation->getMessageTemplate(), $violation->getMessageParameters());
$this->addError($error, $iterator);
}
}
}

View File

@ -16,9 +16,6 @@
<property name="fields">
<constraint name="Valid" />
</property>
<getter property="data">
<constraint name="Valid" />
</getter>
<getter property="submittedWithExtraFields">
<constraint name="AssertFalse">
<option name="message">This form should not contain extra fields</option>

View File

@ -19,6 +19,7 @@ use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormContext;
use Symfony\Component\Form\Field;
use Symfony\Component\Form\FieldError;
use Symfony\Component\Form\DataError;
use Symfony\Component\Form\HiddenField;
use Symfony\Component\Form\PropertyPath;
use Symfony\Component\HttpFoundation\Request;
@ -206,9 +207,19 @@ class FormTest extends \PHPUnit_Framework_TestCase
));
$form->add($field);
$this->validator->expects($this->once())
->method('validate')
->with($this->equalTo($form), $this->equalTo(array('group')));
$this->validator->expects($this->exactly(2))
->method('validate');
// PHPUnit limitation here
// // form data is validated in custom group
// $this->validator->expects($this->once())
// ->method('validate')
// ->with($this->equalTo($form->getData()), $this->equalTo(array('group')));
//
// // form itself is validated in group "Default"
// $this->validator->expects($this->once())
// ->method('validate')
// ->with($this->equalTo($form));
// data is irrelevant
$form->bind($this->createPostRequest());
@ -453,7 +464,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('fields[firstName].data');
$form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
$form->addError(new FieldError('Message'), $path->getIterator());
}
public function testAddErrorMapsFieldValidationErrorsOntoFieldsWithinNestedForms()
@ -472,13 +483,11 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('fields[names].fields[firstName].data');
$form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
$form->addError(new FieldError('Message'), $path->getIterator());
}
public function testAddErrorKeepsFieldValidationErrorsIfFieldNotFound()
{
$error = new FieldError('Message');
$field = $this->createMockField('foo');
$field->expects($this->never())
->method('addError');
@ -488,15 +497,13 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('fields[bar].data');
$form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
$form->addError(new FieldError('Message'), $path->getIterator());
$this->assertEquals(array($error), $form->getErrors());
$this->assertEquals(array(new FieldError('Message')), $form->getErrors());
}
public function testAddErrorKeepsFieldValidationErrorsIfFieldIsHidden()
{
$error = new FieldError('Message');
$field = $this->createMockField('firstName');
$field->expects($this->any())
->method('isHidden')
@ -509,14 +516,14 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('fields[firstName].data');
$form->addError($error, $path->getIterator(), Form::FIELD_ERROR);
$form->addError(new FieldError('Message'), $path->getIterator());
$this->assertEquals(array($error), $form->getErrors());
$this->assertEquals(array(new FieldError('Message')), $form->getErrors());
}
public function testAddErrorMapsDataValidationErrorsOntoFields()
{
$error = new FieldError('Message');
$error = new DataError('Message');
// path is expected to point at "firstName"
$expectedPath = new PropertyPath('firstName');
@ -528,20 +535,18 @@ class FormTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue(new PropertyPath('firstName')));
$field->expects($this->once())
->method('addError')
->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
->with($this->equalTo($error), $this->equalTo($expectedPathIterator));
$form = new Form('author');
$form->add($field);
$path = new PropertyPath('firstName');
$form->addError($error, $path->getIterator(), Form::DATA_ERROR);
$form->addError($error, $path->getIterator());
}
public function testAddErrorKeepsDataValidationErrorsIfFieldNotFound()
{
$error = new FieldError('Message');
$field = $this->createMockField('foo');
$field->expects($this->any())
->method('getPropertyPath')
@ -554,13 +559,11 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('bar');
$form->addError($error, $path->getIterator(), Form::DATA_ERROR);
$form->addError(new DataError('Message'), $path->getIterator());
}
public function testAddErrorKeepsDataValidationErrorsIfFieldIsHidden()
{
$error = new FieldError('Message');
$field = $this->createMockField('firstName');
$field->expects($this->any())
->method('isHidden')
@ -576,12 +579,12 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('firstName');
$form->addError($error, $path->getIterator(), Form::DATA_ERROR);
$form->addError(new DataError('Message'), $path->getIterator());
}
public function testAddErrorMapsDataValidationErrorsOntoNestedFields()
{
$error = new FieldError('Message');
$error = new DataError('Message');
// path is expected to point at "street"
$expectedPath = new PropertyPath('address.street');
@ -594,19 +597,19 @@ class FormTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue(new PropertyPath('address')));
$field->expects($this->once())
->method('addError')
->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
->with($this->equalTo($error), $this->equalTo($expectedPathIterator));
$form = new Form('author');
$form->add($field);
$path = new PropertyPath('address.street');
$form->addError($error, $path->getIterator(), Form::DATA_ERROR);
$form->addError($error, $path->getIterator());
}
public function testAddErrorMapsErrorsOntoFieldsInVirtualGroups()
{
$error = new FieldError('Message');
$error = new DataError('Message');
// path is expected to point at "address"
$expectedPath = new PropertyPath('address');
@ -618,7 +621,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
->will($this->returnValue(new PropertyPath('address')));
$field->expects($this->once())
->method('addError')
->with($this->equalTo($error), $this->equalTo($expectedPathIterator), $this->equalTo(Form::DATA_ERROR));
->with($this->equalTo($error), $this->equalTo($expectedPathIterator));
$form = new Form('author');
$nestedForm = new Form('nested', array('virtual' => true));
@ -627,7 +630,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
$path = new PropertyPath('address');
$form->addError($error, $path->getIterator(), Form::DATA_ERROR);
$form->addError($error, $path->getIterator());
}
public function testAddThrowsExceptionIfAlreadySubmitted()