From c8a0ee6b3a5114bfd922fea54d57c41629e8b615 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Fri, 10 Jan 2014 11:44:16 +0100 Subject: [PATCH] [Form] Errors now reference the field they were added to and the violation/exception that caused them --- .../Resources/views/Collector/form.html.twig | 29 +- src/Symfony/Component/Form/CHANGELOG.md | 2 + .../DataCollector/FormDataCollector.php | 17 +- .../DataCollector/FormDataExtractor.php | 29 +- .../ViolationMapper/ViolationMapper.php | 3 +- src/Symfony/Component/Form/Form.php | 4 + src/Symfony/Component/Form/FormError.php | 86 +++++- .../DataCollector/FormDataCollectorTest.php | 257 +++++++++++------- .../DataCollector/FormDataExtractorTest.php | 52 +++- .../ViolationMapper/ViolationMapperTest.php | 35 +-- 10 files changed, 384 insertions(+), 130 deletions(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index 602ae9668ba2a..bdeb8d7e948fc 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -191,7 +191,7 @@ {% for formName, formData in collector.data.forms %} - {{ form_tree_details(formName, formData) }} + {{ form_tree_details(formName, formData, collector.data.forms_by_hash) }} {% endfor %} {% else %} @@ -366,7 +366,7 @@ {% endmacro %} -{% macro form_tree_details(name, data) %} +{% macro form_tree_details(name, data, forms_by_hash) %}

{{ name }} @@ -386,13 +386,32 @@ - + + {% for error in data.errors %} - + + {% endfor %}
MessageMessageOrigin Cause
{{ error.message }}Unknown. + {% if error.origin is empty %} + This form. + {% elseif forms_by_hash[error.origin] is not defined %} + Unknown. + {% else %} + {{ forms_by_hash[error.origin].name }} + {% endif %} + + {% if error.cause is empty %} + Unknown. + {% elseif error.cause.root is defined %} + Constraint Violation
+
{{ error.cause.root }}{% if error.cause.path is not empty %}{% if error.cause.path|first != '[' %}.{% endif %}{{ error.cause.path }}{% endif %} = {{ error.cause.value }}
+ {% else %} +
{{ error.cause }}
+ {% endif %} +
@@ -565,6 +584,6 @@

{% for childName, childData in data.children %} - {{ _self.form_tree_details(childName, childData) }} + {{ _self.form_tree_details(childName, childData, forms_by_hash) }} {% endfor %} {% endmacro %} diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index b9c8256b5a055..ff4e46284f547 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -5,6 +5,8 @@ CHANGELOG ------ * 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 ----- diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 2119248132963..bcadfc59773ef 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -69,6 +69,7 @@ public function __construct(FormDataExtractorInterface $dataExtractor) $this->dataExtractor = $dataExtractor; $this->data = array( 'forms' => array(), + 'forms_by_hash' => array(), 'nb_errors' => 0, ); } @@ -184,7 +185,7 @@ public function buildPreliminaryFormTree(FormInterface $form) { $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 @@ public function buildFinalFormTree(FormInterface $form, FormView $view) { $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 @@ public function getData() return $this->data; } - private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null) + private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output = null, array &$outputByHash) { $hash = spl_object_hash($form); @@ -221,16 +222,18 @@ private function recursiveBuildPreliminaryFormTree(FormInterface $form, &$output ? $this->dataByForm[$hash] : array(); + $outputByHash[$hash] = &$output; + $output['children'] = array(); foreach ($form as $name => $child) { $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); $formHash = null; @@ -255,6 +258,8 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie ? $this->dataByForm[$formHash] : array() ); + + $outputByHash[$formHash] = &$output; } $output['children'] = array(); @@ -268,7 +273,7 @@ private function recursiveBuildFinalFormTree(FormInterface $form = null, FormVie $output['children'][$name] = array(); - $this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name]); + $this->recursiveBuildFinalFormTree($childForm, $childView, $output['children'][$name], $outputByHash); } } } diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php index 8765b13ca4d0e..80e910177a894 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter; +use Symfony\Component\Validator\ConstraintViolationInterface; /** * Default implementation of {@link FormDataExtractorInterface}. @@ -43,6 +44,7 @@ public function extractConfiguration(FormInterface $form) { $data = array( 'id' => $this->buildId($form), + 'name' => $form->getName(), 'type' => $form->getConfig()->getType()->getName(), 'type_class' => get_class($form->getConfig()->getType()->getInnerType()), 'synchronized' => $this->valueExporter->exportValue($form->isSynchronized()), @@ -108,9 +110,26 @@ public function extractSubmittedData(FormInterface $form) } foreach ($form->getErrors() as $error) { - $data['errors'][] = array( + $errorData = array( '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()); @@ -127,8 +146,12 @@ public function extractViewVariables(FormView $view) // Set the ID in case no FormInterface object was collected for this // view - if (isset($view->vars['id'])) { - $data['id'] = $view->vars['id']; + if (!isset($data['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) { diff --git a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php index 6ea56eb03b242..15ceb0cf08bd7 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php +++ b/src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php @@ -128,7 +128,8 @@ public function mapViolation(ConstraintViolation $violation, FormInterface $form $violation->getMessage(), $violation->getMessageTemplate(), $violation->getMessageParameters(), - $violation->getMessagePluralization() + $violation->getMessagePluralization(), + $violation )); } } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index e677fff62993d..f4696c0dfed34 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -673,6 +673,10 @@ public function bind($submittedData) public function addError(FormError $error) { if ($this->parent && $this->config->getErrorBubbling()) { + if (null === $error->getOrigin()) { + $error->setOrigin($this); + } + $this->parent->addError($error); } else { $this->errors[] = $error; diff --git a/src/Symfony/Component/Form/FormError.php b/src/Symfony/Component/Form/FormError.php index 343165ca465d8..6970244cf7c18 100644 --- a/src/Symfony/Component/Form/FormError.php +++ b/src/Symfony/Component/Form/FormError.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Form; +use Symfony\Component\Form\Exception\BadMethodCallException; + /** * Wraps errors in forms * * @author Bernhard Schussek */ -class FormError +class FormError implements \Serializable { /** * @var string @@ -41,6 +43,18 @@ class FormError */ protected $messagePluralization; + /** + * The cause for this error + * @var mixed + */ + private $cause; + + /** + * The form that spawned this error + * @var FormInterface + */ + private $origin; + /** * Constructor * @@ -50,17 +64,19 @@ class FormError * @param string $message The translated error message * @param string|null $messageTemplate The template for the error message * @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 mixed $cause The cause of the error * * @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->messageTemplate = $messageTemplate ?: $message; $this->messageParameters = $messageParameters; $this->messagePluralization = $messagePluralization; + $this->cause = $cause; } /** @@ -102,4 +118,68 @@ public function getMessagePluralization() { 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); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php index 99ac76ac23933..885b4b68d1c43 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataCollectorTest.php @@ -111,23 +111,31 @@ public function testBuildPreliminaryFormTree() $this->dataCollector->collectSubmittedData($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form); - $this->assertSame(array( - 'forms' => array( - 'name' => array( - 'config' => 'foo', - 'default_data' => 'foo', - 'submitted_data' => 'foo', - 'children' => array( - 'child' => array( - 'config' => 'bar', - 'default_data' => 'bar', - 'submitted_data' => 'bar', - 'children' => array(), - ), - ), - ), + $childFormData = array( + 'config' => 'bar', + 'default_data' => 'bar', + 'submitted_data' => 'bar', + 'children' => array(), + ); + + $formData = array( + 'config' => 'foo', + 'default_data' => 'foo', + 'submitted_data' => 'foo', + '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()); } @@ -149,28 +157,36 @@ public function testBuildMultiplePreliminaryFormTrees() $this->dataCollector->collectConfiguration($form2); $this->dataCollector->buildPreliminaryFormTree($form1); + $form1Data = array( + 'config' => 'foo', + 'children' => array(), + ); + $this->assertSame(array( 'forms' => array( - 'form1' => array( - 'config' => 'foo', - 'children' => array(), - ), + 'form1' => $form1Data, + ), + 'forms_by_hash' => array( + spl_object_hash($form1) => $form1Data, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); $this->dataCollector->buildPreliminaryFormTree($form2); + $form2Data = array( + 'config' => 'bar', + 'children' => array(), + ); + $this->assertSame(array( 'forms' => array( - 'form1' => array( - 'config' => 'foo', - 'children' => array(), - ), - 'form2' => array( - 'config' => 'bar', - 'children' => array(), - ), + 'form1' => $form1Data, + 'form2' => $form2Data, + ), + 'forms_by_hash' => array( + spl_object_hash($form1) => $form1Data, + spl_object_hash($form2) => $form2Data, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -191,12 +207,17 @@ public function testBuildSamePreliminaryFormTreeMultipleTimes() $this->dataCollector->collectConfiguration($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form); + $formData = array( + 'config' => 'foo', + 'children' => array(), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'config' => 'foo', - 'children' => array(), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -204,13 +225,18 @@ public function testBuildSamePreliminaryFormTreeMultipleTimes() $this->dataCollector->collectDefaultData($this->form); $this->dataCollector->buildPreliminaryFormTree($this->form); + $formData = array( + 'config' => 'foo', + 'default_data' => 'foo', + 'children' => array(), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'config' => 'foo', - 'default_data' => 'foo', - 'children' => array(), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -220,11 +246,16 @@ public function testBuildPreliminaryFormTreeWithoutCollectingAnyData() { $this->dataCollector->buildPreliminaryFormTree($this->form); + $formData = array( + 'children' => array(), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'children' => array(), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -278,23 +309,31 @@ public function testBuildFinalFormTree() $this->dataCollector->collectViewVariables($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( 'forms' => array( - 'name' => array( - 'view_vars' => 'foo', - 'config' => 'foo', - 'default_data' => 'foo', - 'submitted_data' => 'foo', - 'children' => array( - 'child' => array( - 'view_vars' => 'bar', - 'config' => 'bar', - 'default_data' => 'bar', - 'submitted_data' => 'bar', - 'children' => 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()); @@ -302,41 +341,57 @@ public function testBuildFinalFormTree() public function testFinalFormReliesOnFormViewStructure() { - $this->form->add($this->createForm('first')); - $this->form->add($this->createForm('second')); + $this->form->add($child1 = $this->createForm('first')); + $this->form->add($child2 = $this->createForm('second')); $this->view->children['second'] = $this->childView; $this->dataCollector->buildPreliminaryFormTree($this->form); + $child1Data = array( + 'children' => array(), + ); + + $child2Data = array( + 'children' => array(), + ); + + $formData = array( + 'children' => array( + 'first' => $child1Data, + 'second' => $child2Data, + ), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'children' => array( - 'first' => array( - 'children' => array(), - ), - 'second' => array( - 'children' => array(), - ), - ), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($child1) => $child1Data, + spl_object_hash($child2) => $child2Data, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); $this->dataCollector->buildFinalFormTree($this->form, $this->view); + $formData = array( + 'children' => array( + // "first" not present in FormView + 'second' => $child2Data, + ), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'children' => array( - // "first" not present in FormView - 'second' => array( - 'children' => array(), - ), - ), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + spl_object_hash($child1) => $child1Data, + spl_object_hash($child2) => $child2Data, ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -363,17 +418,25 @@ public function testChildViewsCanBeWithoutCorrespondingChildForms() $this->dataCollector->collectConfiguration($this->childForm); $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( 'forms' => array( - 'name' => array( - 'config' => 'foo', - 'children' => array( - 'child' => array( - // no "config" key - 'children' => array(), - ), - ), - ), + 'name' => $formData, + ), + 'forms_by_hash' => array( + spl_object_hash($this->form) => $formData, + // no child entry ), 'nb_errors' => 0, ), $this->dataCollector->getData()); @@ -403,17 +466,25 @@ public function testChildViewsWithoutCorrespondingChildFormsMayBeExplicitlyAssoc $this->dataCollector->collectConfiguration($this->childForm); $this->dataCollector->buildFinalFormTree($this->form, $this->view); + $childFormData = array( + 'config' => 'bar', + 'children' => array(), + ); + + $formData = array( + 'config' => 'foo', + 'children' => array( + 'child' => $childFormData, + ), + ); + $this->assertSame(array( 'forms' => array( - 'name' => array( - 'config' => 'foo', - 'children' => array( - 'child' => array( - 'config' => 'bar', - 'children' => 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()); diff --git a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php index bf3cd71975663..37b5b7d86cfbb 100644 --- a/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/DataCollector/FormDataExtractorTest.php @@ -27,7 +27,7 @@ class FormDataExtractorTest_SimpleValueExporter extends ValueExporter */ 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 @@ public function testExtractConfiguration() $this->assertSame(array( 'id' => 'name', + 'name' => 'name', 'type' => 'type_name', 'type_class' => 'stdClass', 'synchronized' => 'true', @@ -113,6 +114,7 @@ public function testExtractConfigurationSortsPassedOptions() $this->assertSame(array( 'id' => 'name', + 'name' => 'name', 'type' => 'type_name', 'type_class' => 'stdClass', 'synchronized' => 'true', @@ -147,6 +149,7 @@ public function testExtractConfigurationSortsResolvedOptions() $this->assertSame(array( 'id' => 'name', + 'name' => 'name', 'type' => 'type_name', 'type_class' => 'stdClass', 'synchronized' => 'true', @@ -186,6 +189,7 @@ public function testExtractConfigurationBuildsIdRecursively() $this->assertSame(array( 'id' => 'grandParent_parent_name', + 'name' => 'name', 'type' => 'type_name', 'type_class' => 'stdClass', 'synchronized' => 'true', @@ -315,7 +319,48 @@ public function testExtractSubmittedDataStoresErrors() 'norm' => "'Foobar'", ), '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', ), $this->dataExtractor->extractSubmittedData($form)); @@ -353,15 +398,18 @@ public function testExtractViewVariables() 'a' => 'bar', 'c' => 'baz', 'id' => 'foo_bar', + 'name' => 'bar', ); $this->assertSame(array( 'id' => 'foo_bar', + 'name' => 'bar', 'view_vars' => array( 'a' => "'bar'", 'b' => "'foo'", 'c' => "'baz'", 'id' => "'foo_bar'", + 'name' => "'bar'", ), ), $this->dataExtractor->extractViewVariables($view)); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php index a84d5b951ae14..3c9886d7cf233 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Form\FormError; use Symfony\Component\PropertyAccess\PropertyPath; use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationInterface; /** * @author Bernhard Schussek @@ -109,9 +110,9 @@ protected function getConstraintViolation($propertyPath) /** * @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() @@ -127,7 +128,7 @@ public function testMapToFormInheritingParentDataIfDataDoesNotMatch() $this->mapper->mapViolation($violation, $parent); $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'); } @@ -154,7 +155,7 @@ public function testFollowDotRules() $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, $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() @@ -745,17 +746,17 @@ public function testDefaultErrorMapping($target, $childName, $childPath, $grandC $this->mapper->mapViolation($violation, $parent); 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, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); } elseif (self::LEVEL_1 === $target) { $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'); } else { $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->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 @@ public function testCustomDataErrorMapping($target, $mapFrom, $mapTo, $childName } 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, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); } elseif (self::LEVEL_1 === $target) { $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'); } else { $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->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 @@ public function testCustomFormErrorMapping($target, $mapFrom, $mapTo, $errorName if (self::LEVEL_0 === $target) { $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, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); } elseif (self::LEVEL_1 === $target) { $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->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'); } 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, $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'); @@ -1416,7 +1417,7 @@ public function testCustomFormErrorMapping($target, $mapFrom, $mapTo, $errorName $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, $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 @@ public function testErrorMappingForFormInheritingParentData($target, $childName, $this->mapper->mapViolation($violation, $parent); 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, $grandChild->getErrors(), $grandChildName.' should not have an error, but has one'); } elseif (self::LEVEL_1 === $target) { $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'); } else { $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->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'); } } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy