[Form] Errors now reference the field they were added to and the violation/exception that caused them

This commit is contained in:
Bernhard Schussek 2014-01-10 11:44:16 +01:00
parent a596ba362b
commit c8a0ee6b3a
10 changed files with 384 additions and 130 deletions

View File

@ -191,7 +191,7 @@
</div> </div>
{% for formName, formData in collector.data.forms %} {% for formName, formData in collector.data.forms %}
{{ form_tree_details(formName, formData) }} {{ form_tree_details(formName, formData, collector.data.forms_by_hash) }}
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
@ -366,7 +366,7 @@
</li> </li>
{% endmacro %} {% endmacro %}
{% macro form_tree_details(name, data) %} {% macro form_tree_details(name, data, forms_by_hash) %}
<div class="tree-details" id="{{ data.id }}-details"> <div class="tree-details" id="{{ data.id }}-details">
<h2> <h2>
{{ name }} {{ name }}
@ -386,13 +386,32 @@
<table id="{{ data.id }}-errors"> <table id="{{ data.id }}-errors">
<tr> <tr>
<th width="50%">Message</th> <th>Message</th>
<th>Origin</th>
<th>Cause</th> <th>Cause</th>
</tr> </tr>
{% for error in data.errors %} {% for error in data.errors %}
<tr> <tr>
<td>{{ error.message }}</td> <td>{{ error.message }}</td>
<td><em>Unknown.</em></td> <td>
{% if error.origin is empty %}
<em>This form.</em>
{% elseif forms_by_hash[error.origin] is not defined %}
<em>Unknown.</em>
{% else %}
{{ forms_by_hash[error.origin].name }}
{% endif %}
</td>
<td>
{% if error.cause is empty %}
<em>Unknown.</em>
{% elseif error.cause.root is defined %}
<strong>Constraint Violation</strong><br/>
<pre>{{ error.cause.root }}{% if error.cause.path is not empty %}{% if error.cause.path|first != '[' %}.{% endif %}{{ error.cause.path }}{% endif %} = {{ error.cause.value }}</pre>
{% else %}
<pre>{{ error.cause }}</pre>
{% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@ -565,6 +584,6 @@
</div> </div>
{% for childName, childData in data.children %} {% for childName, childData in data.children %}
{{ _self.form_tree_details(childName, childData) }} {{ _self.form_tree_details(childName, childData, forms_by_hash) }}
{% endfor %} {% endfor %}
{% endmacro %} {% endmacro %}

View File

@ -5,6 +5,8 @@ CHANGELOG
------ ------
* added an option for multiple files upload * added an option for multiple files upload
* form errors now reference their cause (constraint violation, exception, ...)
* form errors now remember which form they were originally added to
2.4.0 2.4.0
----- -----

View File

@ -69,6 +69,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
$this->dataExtractor = $dataExtractor; $this->dataExtractor = $dataExtractor;
$this->data = array( $this->data = array(
'forms' => array(), 'forms' => array(),
'forms_by_hash' => array(),
'nb_errors' => 0, 'nb_errors' => 0,
); );
} }
@ -184,7 +185,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
{ {
$this->data['forms'][$form->getName()] = array(); $this->data['forms'][$form->getName()] = array();
$this->recursiveBuildPreliminaryFormTree($form, $this->data['forms'][$form->getName()]); $this->recursiveBuildPreliminaryFormTree($form, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']);
} }
/** /**
@ -194,7 +195,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
{ {
$this->data['forms'][$form->getName()] = array(); $this->data['forms'][$form->getName()] = array();
$this->recursiveBuildFinalFormTree($form, $view, $this->data['forms'][$form->getName()]); $this->recursiveBuildFinalFormTree($form, $view, $this->data['forms'][$form->getName()], $this->data['forms_by_hash']);
} }
/** /**
@ -213,7 +214,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
return $this->data; return $this->data;
} }
private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null) private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null, array &$outputByHash)
{ {
$hash = spl_object_hash($form); $hash = spl_object_hash($form);
@ -221,16 +222,18 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
? $this->dataByForm[$hash] ? $this->dataByForm[$hash]
: array(); : array();
$outputByHash[$hash] = &$output;
$output['children'] = array(); $output['children'] = array();
foreach ($form as $name => $child) { foreach ($form as $name => $child) {
$output['children'][$name] = array(); $output['children'][$name] = array();
$this->recursiveBuildPreliminaryFormTree($child, $output['children'][$name]); $this->recursiveBuildPreliminaryFormTree($child, $output['children'][$name], $outputByHash);
} }
} }
private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output = null) private function recursiveBuildFinalFormTree(FormInterface $form = null, FormView $view, &$output = null, array &$outputByHash)
{ {
$viewHash = spl_object_hash($view); $viewHash = spl_object_hash($view);
$formHash = null; $formHash = null;
@ -255,6 +258,8 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
? $this->dataByForm[$formHash] ? $this->dataByForm[$formHash]
: array() : array()
); );
$outputByHash[$formHash] = &$output;
} }
$output['children'] = array(); $output['children'] = array();
@ -268,7 +273,7 @@ class FormDataCollector extends DataCollector implements FormDataCollectorInterf
$output['children'][$name] = array(); $output['children'][$name] = array();
$this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name]); $this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name], $outputByHash);
} }
} }
} }

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Form\Extension\DataCollector;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormView;
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
use Symfony\Component\Validator\ConstraintViolationInterface;
/** /**
* Default implementation of {@link FormDataExtractorInterface}. * Default implementation of {@link FormDataExtractorInterface}.
@ -43,6 +44,7 @@ class FormDataExtractor implements FormDataExtractorInterface
{ {
$data = array( $data = array(
'id' => $this->buildId($form), 'id' => $this->buildId($form),
'name' => $form->getName(),
'type' => $form->getConfig()->getType()->getName(), 'type' => $form->getConfig()->getType()->getName(),
'type_class' => get_class($form->getConfig()->getType()->getInnerType()), 'type_class' => get_class($form->getConfig()->getType()->getInnerType()),
'synchronized' => $this->valueExporter->exportValue($form->isSynchronized()), 'synchronized' => $this->valueExporter->exportValue($form->isSynchronized()),
@ -108,9 +110,26 @@ class FormDataExtractor implements FormDataExtractorInterface
} }
foreach ($form->getErrors() as $error) { foreach ($form->getErrors() as $error) {
$data['errors'][] = array( $errorData = array(
'message' => $error->getMessage(), 'message' => $error->getMessage(),
'origin' => is_object($error->getOrigin())
? spl_object_hash($error->getOrigin())
: null,
); );
$cause = $error->getCause();
if ($cause instanceof ConstraintViolationInterface) {
$errorData['cause'] = array(
'root' => $this->valueExporter->exportValue($cause->getRoot()),
'path' => $this->valueExporter->exportValue($cause->getPropertyPath()),
'value' => $this->valueExporter->exportValue($cause->getInvalidValue()),
);
} else {
$errorData['cause'] = null !== $cause ? $this->valueExporter->exportValue($cause) : null;
}
$data['errors'][] = $errorData;
} }
$data['synchronized'] = $this->valueExporter->exportValue($form->isSynchronized()); $data['synchronized'] = $this->valueExporter->exportValue($form->isSynchronized());
@ -127,8 +146,12 @@ class FormDataExtractor implements FormDataExtractorInterface
// Set the ID in case no FormInterface object was collected for this // Set the ID in case no FormInterface object was collected for this
// view // view
if (isset($view->vars['id'])) { if (!isset($data['id'])) {
$data['id'] = $view->vars['id']; $data['id'] = isset($view->vars['id']) ? $view->vars['id'] : null;
}
if (!isset($data['name'])) {
$data['name'] = isset($view->vars['name']) ? $view->vars['name'] : null;
} }
foreach ($view->vars as $varName => $value) { foreach ($view->vars as $varName => $value) {

View File

@ -128,7 +128,8 @@ class ViolationMapper implements ViolationMapperInterface
$violation->getMessage(), $violation->getMessage(),
$violation->getMessageTemplate(), $violation->getMessageTemplate(),
$violation->getMessageParameters(), $violation->getMessageParameters(),
$violation->getMessagePluralization() $violation->getMessagePluralization(),
$violation
)); ));
} }
} }

View File

@ -673,6 +673,10 @@ class Form implements \IteratorAggregate, FormInterface
public function addError(FormError $error) public function addError(FormError $error)
{ {
if ($this->parent && $this->config->getErrorBubbling()) { if ($this->parent && $this->config->getErrorBubbling()) {
if (null === $error->getOrigin()) {
$error->setOrigin($this);
}
$this->parent->addError($error); $this->parent->addError($error);
} else { } else {
$this->errors[] = $error; $this->errors[] = $error;

View File

@ -11,12 +11,14 @@
namespace Symfony\Component\Form; namespace Symfony\Component\Form;
use Symfony\Component\Form\Exception\BadMethodCallException;
/** /**
* Wraps errors in forms * Wraps errors in forms
* *
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
*/ */
class FormError class FormError implements \Serializable
{ {
/** /**
* @var string * @var string
@ -41,6 +43,18 @@ class FormError
*/ */
protected $messagePluralization; protected $messagePluralization;
/**
* The cause for this error
* @var mixed
*/
private $cause;
/**
* The form that spawned this error
* @var FormInterface
*/
private $origin;
/** /**
* Constructor * Constructor
* *
@ -50,17 +64,19 @@ class FormError
* @param string $message The translated error message * @param string $message The translated error message
* @param string|null $messageTemplate The template for the error message * @param string|null $messageTemplate The template for the error message
* @param array $messageParameters The parameters that should be * @param array $messageParameters The parameters that should be
* substituted in the message template. * substituted in the message template
* @param integer|null $messagePluralization The value for error message pluralization * @param integer|null $messagePluralization The value for error message pluralization
* @param mixed $cause The cause of the error
* *
* @see \Symfony\Component\Translation\Translator * @see \Symfony\Component\Translation\Translator
*/ */
public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null) public function __construct($message, $messageTemplate = null, array $messageParameters = array(), $messagePluralization = null, $cause = null)
{ {
$this->message = $message; $this->message = $message;
$this->messageTemplate = $messageTemplate ?: $message; $this->messageTemplate = $messageTemplate ?: $message;
$this->messageParameters = $messageParameters; $this->messageParameters = $messageParameters;
$this->messagePluralization = $messagePluralization; $this->messagePluralization = $messagePluralization;
$this->cause = $cause;
} }
/** /**
@ -102,4 +118,68 @@ class FormError
{ {
return $this->messagePluralization; return $this->messagePluralization;
} }
/**
* Returns the cause of this error.
*
* @return mixed The cause of this error
*/
public function getCause()
{
return $this->cause;
}
/**
* Sets the form that caused this error.
*
* This method must only be called once.
*
* @param FormInterface $origin The form that caused this error
*
* @throws BadMethodCallException If the method is called more than once
*/
public function setOrigin(FormInterface $origin)
{
if (null !== $this->origin) {
throw new BadMethodCallException('setOrigin() must only be called once.');
}
$this->origin = $origin;
}
/**
* Returns the form that caused this error.
*
* @return FormInterface The form that caused this error
*/
public function getOrigin()
{
return $this->origin;
}
/**
* Serializes this error.
*
* @return string The serialized error
*/
public function serialize()
{
return serialize(array(
$this->message,
$this->messageTemplate,
$this->messageParameters,
$this->messagePluralization,
$this->cause
));
}
/**
* Unserializes a serialized error.
*
* @param string $serialized The serialized error
*/
public function unserialize($serialized)
{
list($this->message, $this->messageTemplate, $this->messageParameters, $this->messagePluralization, $this->cause) = unserialize($serialized);
}
} }

View File

@ -111,23 +111,31 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectSubmittedData($this->form); $this->dataCollector->collectSubmittedData($this->form);
$this->dataCollector->buildPreliminaryFormTree($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form);
$this->assertSame(array( $childFormData = array(
'forms' => array( 'config' => 'bar',
'name' => array( 'default_data' => 'bar',
'config' => 'foo', 'submitted_data' => 'bar',
'default_data' => 'foo', 'children' => array(),
'submitted_data' => 'foo', );
'children' => array(
'child' => array( $formData = array(
'config' => 'bar', 'config' => 'foo',
'default_data' => 'bar', 'default_data' => 'foo',
'submitted_data' => 'bar', 'submitted_data' => 'foo',
'children' => array(), 'children' => array(
), 'child' => $childFormData,
),
),
), ),
'nb_errors' => 0, );
$this->assertSame(array(
'forms' => array(
'name' => $formData,
),
'forms_by_hash' => array(
spl_object_hash($this->form) => $formData,
spl_object_hash($this->childForm) => $childFormData,
),
'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
} }
@ -149,28 +157,36 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectConfiguration($form2); $this->dataCollector->collectConfiguration($form2);
$this->dataCollector->buildPreliminaryFormTree($form1); $this->dataCollector->buildPreliminaryFormTree($form1);
$form1Data = array(
'config' => 'foo',
'children' => array(),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'form1' => array( 'form1' => $form1Data,
'config' => 'foo', ),
'children' => array(), 'forms_by_hash' => array(
), spl_object_hash($form1) => $form1Data,
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
$this->dataCollector->buildPreliminaryFormTree($form2); $this->dataCollector->buildPreliminaryFormTree($form2);
$form2Data = array(
'config' => 'bar',
'children' => array(),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'form1' => array( 'form1' => $form1Data,
'config' => 'foo', 'form2' => $form2Data,
'children' => array(), ),
), 'forms_by_hash' => array(
'form2' => array( spl_object_hash($form1) => $form1Data,
'config' => 'bar', spl_object_hash($form2) => $form2Data,
'children' => array(),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -191,12 +207,17 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectConfiguration($this->form); $this->dataCollector->collectConfiguration($this->form);
$this->dataCollector->buildPreliminaryFormTree($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form);
$formData = array(
'config' => 'foo',
'children' => array(),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'config' => 'foo', ),
'children' => array(), 'forms_by_hash' => array(
), spl_object_hash($this->form) => $formData,
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -204,13 +225,18 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectDefaultData($this->form); $this->dataCollector->collectDefaultData($this->form);
$this->dataCollector->buildPreliminaryFormTree($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form);
$formData = array(
'config' => 'foo',
'default_data' => 'foo',
'children' => array(),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'config' => 'foo', ),
'default_data' => 'foo', 'forms_by_hash' => array(
'children' => array(), spl_object_hash($this->form) => $formData,
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -220,11 +246,16 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
{ {
$this->dataCollector->buildPreliminaryFormTree($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form);
$formData = array(
'children' => array(),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'children' => array(), ),
), 'forms_by_hash' => array(
spl_object_hash($this->form) => $formData,
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -278,23 +309,31 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectViewVariables($this->view); $this->dataCollector->collectViewVariables($this->view);
$this->dataCollector->buildFinalFormTree($this->form, $this->view); $this->dataCollector->buildFinalFormTree($this->form, $this->view);
$childFormData = array(
'view_vars' => 'bar',
'config' => 'bar',
'default_data' => 'bar',
'submitted_data' => 'bar',
'children' => array(),
);
$formData = array(
'view_vars' => 'foo',
'config' => 'foo',
'default_data' => 'foo',
'submitted_data' => 'foo',
'children' => array(
'child' => $childFormData,
),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'view_vars' => 'foo', ),
'config' => 'foo', 'forms_by_hash' => array(
'default_data' => 'foo', spl_object_hash($this->form) => $formData,
'submitted_data' => 'foo', spl_object_hash($this->childForm) => $childFormData,
'children' => array(
'child' => array(
'view_vars' => 'bar',
'config' => 'bar',
'default_data' => 'bar',
'submitted_data' => 'bar',
'children' => array(),
),
),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -302,41 +341,57 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
public function testFinalFormReliesOnFormViewStructure() public function testFinalFormReliesOnFormViewStructure()
{ {
$this->form->add($this->createForm('first')); $this->form->add($child1 = $this->createForm('first'));
$this->form->add($this->createForm('second')); $this->form->add($child2 = $this->createForm('second'));
$this->view->children['second'] = $this->childView; $this->view->children['second'] = $this->childView;
$this->dataCollector->buildPreliminaryFormTree($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form);
$child1Data = array(
'children' => array(),
);
$child2Data = array(
'children' => array(),
);
$formData = array(
'children' => array(
'first' => $child1Data,
'second' => $child2Data,
),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'children' => array( ),
'first' => array( 'forms_by_hash' => array(
'children' => array(), spl_object_hash($this->form) => $formData,
), spl_object_hash($child1) => $child1Data,
'second' => array( spl_object_hash($child2) => $child2Data,
'children' => array(),
),
),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
$this->dataCollector->buildFinalFormTree($this->form, $this->view); $this->dataCollector->buildFinalFormTree($this->form, $this->view);
$formData = array(
'children' => array(
// "first" not present in FormView
'second' => $child2Data,
),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'children' => array( ),
// "first" not present in FormView 'forms_by_hash' => array(
'second' => array( spl_object_hash($this->form) => $formData,
'children' => array(), spl_object_hash($child1) => $child1Data,
), spl_object_hash($child2) => $child2Data,
),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -363,17 +418,25 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectConfiguration($this->childForm); $this->dataCollector->collectConfiguration($this->childForm);
$this->dataCollector->buildFinalFormTree($this->form, $this->view); $this->dataCollector->buildFinalFormTree($this->form, $this->view);
$childFormData = array(
// no "config" key
'children' => array(),
);
$formData = array(
'config' => 'foo',
'children' => array(
'child' => $childFormData,
),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'config' => 'foo', ),
'children' => array( 'forms_by_hash' => array(
'child' => array( spl_object_hash($this->form) => $formData,
// no "config" key // no child entry
'children' => array(),
),
),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());
@ -403,17 +466,25 @@ class FormDataCollectorTest extends \PHPUnit_Framework_TestCase
$this->dataCollector->collectConfiguration($this->childForm); $this->dataCollector->collectConfiguration($this->childForm);
$this->dataCollector->buildFinalFormTree($this->form, $this->view); $this->dataCollector->buildFinalFormTree($this->form, $this->view);
$childFormData = array(
'config' => 'bar',
'children' => array(),
);
$formData = array(
'config' => 'foo',
'children' => array(
'child' => $childFormData,
),
);
$this->assertSame(array( $this->assertSame(array(
'forms' => array( 'forms' => array(
'name' => array( 'name' => $formData,
'config' => 'foo', ),
'children' => array( 'forms_by_hash' => array(
'child' => array( spl_object_hash($this->form) => $formData,
'config' => 'bar', spl_object_hash($this->childForm) => $childFormData,
'children' => array(),
),
),
),
), ),
'nb_errors' => 0, 'nb_errors' => 0,
), $this->dataCollector->getData()); ), $this->dataCollector->getData());

View File

@ -27,7 +27,7 @@ class FormDataExtractorTest_SimpleValueExporter extends ValueExporter
*/ */
public function exportValue($value) public function exportValue($value)
{ {
return var_export($value, true); return is_object($value) ? sprintf('object(%s)', get_class($value)) : var_export($value, true);
} }
} }
@ -80,6 +80,7 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array( $this->assertSame(array(
'id' => 'name', 'id' => 'name',
'name' => 'name',
'type' => 'type_name', 'type' => 'type_name',
'type_class' => 'stdClass', 'type_class' => 'stdClass',
'synchronized' => 'true', 'synchronized' => 'true',
@ -113,6 +114,7 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array( $this->assertSame(array(
'id' => 'name', 'id' => 'name',
'name' => 'name',
'type' => 'type_name', 'type' => 'type_name',
'type_class' => 'stdClass', 'type_class' => 'stdClass',
'synchronized' => 'true', 'synchronized' => 'true',
@ -147,6 +149,7 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array( $this->assertSame(array(
'id' => 'name', 'id' => 'name',
'name' => 'name',
'type' => 'type_name', 'type' => 'type_name',
'type_class' => 'stdClass', 'type_class' => 'stdClass',
'synchronized' => 'true', 'synchronized' => 'true',
@ -186,6 +189,7 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array( $this->assertSame(array(
'id' => 'grandParent_parent_name', 'id' => 'grandParent_parent_name',
'name' => 'name',
'type' => 'type_name', 'type' => 'type_name',
'type_class' => 'stdClass', 'type_class' => 'stdClass',
'synchronized' => 'true', 'synchronized' => 'true',
@ -315,7 +319,48 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
'norm' => "'Foobar'", 'norm' => "'Foobar'",
), ),
'errors' => array( 'errors' => array(
array('message' => 'Invalid!'), array('message' => 'Invalid!', 'origin' => null, 'cause' => null),
),
'synchronized' => 'true',
), $this->dataExtractor->extractSubmittedData($form));
}
public function testExtractSubmittedDataStoresErrorOrigin()
{
$form = $this->createBuilder('name')->getForm();
$error = new FormError('Invalid!');
$error->setOrigin($form);
$form->submit('Foobar');
$form->addError($error);
$this->assertSame(array(
'submitted_data' => array(
'norm' => "'Foobar'",
),
'errors' => array(
array('message' => 'Invalid!', 'origin' => spl_object_hash($form), 'cause' => null),
),
'synchronized' => 'true',
), $this->dataExtractor->extractSubmittedData($form));
}
public function testExtractSubmittedDataStoresErrorCause()
{
$form = $this->createBuilder('name')->getForm();
$exception = new \Exception();
$form->submit('Foobar');
$form->addError(new FormError('Invalid!', null, array(), null, $exception));
$this->assertSame(array(
'submitted_data' => array(
'norm' => "'Foobar'",
),
'errors' => array(
array('message' => 'Invalid!', 'origin' => null, 'cause' => 'object(Exception)'),
), ),
'synchronized' => 'true', 'synchronized' => 'true',
), $this->dataExtractor->extractSubmittedData($form)); ), $this->dataExtractor->extractSubmittedData($form));
@ -353,15 +398,18 @@ class FormDataExtractorTest extends \PHPUnit_Framework_TestCase
'a' => 'bar', 'a' => 'bar',
'c' => 'baz', 'c' => 'baz',
'id' => 'foo_bar', 'id' => 'foo_bar',
'name' => 'bar',
); );
$this->assertSame(array( $this->assertSame(array(
'id' => 'foo_bar', 'id' => 'foo_bar',
'name' => 'bar',
'view_vars' => array( 'view_vars' => array(
'a' => "'bar'", 'a' => "'bar'",
'b' => "'foo'", 'b' => "'foo'",
'c' => "'baz'", 'c' => "'baz'",
'id' => "'foo_bar'", 'id' => "'foo_bar'",
'name' => "'bar'",
), ),
), $this->dataExtractor->extractViewVariables($view)); ), $this->dataExtractor->extractViewVariables($view));
} }

View File

@ -19,6 +19,7 @@ use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\PropertyAccess\PropertyPath; use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolation;
use Symfony\Component\Validator\ConstraintViolationInterface;
/** /**
* @author Bernhard Schussek <bschussek@gmail.com> * @author Bernhard Schussek <bschussek@gmail.com>
@ -109,9 +110,9 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
/** /**
* @return FormError * @return FormError
*/ */
protected function getFormError() protected function getFormError(ConstraintViolationInterface $violation)
{ {
return new FormError($this->message, $this->messageTemplate, $this->params); return new FormError($this->message, $this->messageTemplate, $this->params, null, $violation);
} }
public function testMapToFormInheritingParentDataIfDataDoesNotMatch() public function testMapToFormInheritingParentDataIfDataDoesNotMatch()
@ -127,7 +128,7 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
$this->mapper->mapViolation($violation, $parent); $this->mapper->mapViolation($violation, $parent);
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $child->getErrors(), $child->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $child->getErrors(), $child->getName().' should have an error, but has none');
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
} }
@ -154,7 +155,7 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $child->getName().' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChild->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $grandGrandChild->getErrors(), $grandGrandChild->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $grandGrandChild->getErrors(), $grandGrandChild->getName().' should have an error, but has none');
} }
public function testAbortMappingIfNotSynchronized() public function testAbortMappingIfNotSynchronized()
@ -745,17 +746,17 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
$this->mapper->mapViolation($violation, $parent); $this->mapper->mapViolation($violation, $parent);
if (self::LEVEL_0 === $target) { if (self::LEVEL_0 === $target) {
$this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $parent->getErrors(), $parent->getName().' should have an error, but has none');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} elseif (self::LEVEL_1 === $target) { } elseif (self::LEVEL_1 === $target) {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $child->getErrors(), $childName.' should have an error, but has none');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} else { } else {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $grandChild->getErrors(), $grandChildName.' should have an error, but has none');
} }
} }
@ -1217,17 +1218,17 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
} }
if (self::LEVEL_0 === $target) { if (self::LEVEL_0 === $target) {
$this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $parent->getErrors(), $parent->getName().' should have an error, but has none');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} elseif (self::LEVEL_1 === $target) { } elseif (self::LEVEL_1 === $target) {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $child->getErrors(), $childName.' should have an error, but has none');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} else { } else {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $grandChild->getErrors(), $grandChildName.' should have an error, but has none');
} }
} }
@ -1399,16 +1400,16 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
if (self::LEVEL_0 === $target) { if (self::LEVEL_0 === $target) {
$this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $parent->getErrors(), $parent->getName().' should have an error, but has none');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} elseif (self::LEVEL_1 === $target) { } elseif (self::LEVEL_1 === $target) {
$this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one');
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $child->getErrors(), $childName.' should have an error, but has none');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} elseif (self::LEVEL_1B === $target) { } elseif (self::LEVEL_1B === $target) {
$this->assertEquals(array($this->getFormError()), $errorChild->getErrors(), $errorName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $errorChild->getErrors(), $errorName.' should have an error, but has none');
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
@ -1416,7 +1417,7 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
$this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one'); $this->assertCount(0, $errorChild->getErrors(), $errorName.' should not have an error, but has one');
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $grandChild->getErrors(), $grandChildName.' should have an error, but has none');
} }
} }
@ -1461,17 +1462,17 @@ class ViolationMapperTest extends \PHPUnit_Framework_TestCase
$this->mapper->mapViolation($violation, $parent); $this->mapper->mapViolation($violation, $parent);
if (self::LEVEL_0 === $target) { if (self::LEVEL_0 === $target) {
$this->assertEquals(array($this->getFormError()), $parent->getErrors(), $parent->getName().' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $parent->getErrors(), $parent->getName().' should have an error, but has none');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} elseif (self::LEVEL_1 === $target) { } elseif (self::LEVEL_1 === $target) {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $child->getErrors(), $childName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $child->getErrors(), $childName.' should have an error, but has none');
$this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); $this->assertCount(0, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one');
} else { } else {
$this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one'); $this->assertCount(0, $parent->getErrors(), $parent->getName().' should not have an error, but has one');
$this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one'); $this->assertCount(0, $child->getErrors(), $childName.' should not have an error, but has one');
$this->assertEquals(array($this->getFormError()), $grandChild->getErrors(), $grandChildName.' should have an error, but has none'); $this->assertEquals(array($this->getFormError($violation)), $grandChild->getErrors(), $grandChildName.' should have an error, but has none');
} }
} }
} }