Skip to content

Commit ae6c88d

Browse files
committed
[Form] The trace of form errors is now displayed in the profiler
1 parent 499eeb4 commit ae6c88d

File tree

7 files changed

+87
-22
lines changed

7 files changed

+87
-22
lines changed

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -473,14 +473,28 @@
473473
{% endif %}
474474
</td>
475475
<td>
476-
{% if error.cause is empty %}
477-
<em>Unknown.</em>
478-
{% elseif error.cause.root is defined %}
479-
<strong>Constraint Violation</strong><br/>
480-
<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>
476+
{% for trace in error.trace %}
477+
{% if not loop.first %}
478+
<br/>Caused by:<br/><br/>
479+
{% endif %}
480+
{% if trace.root is defined %}
481+
<strong>{{ trace.class }}</strong><br/>
482+
<pre>
483+
{{- trace.root -}}
484+
{%- if trace.path is not empty -%}
485+
{%- if trace.path|first != '[' %}.{% endif -%}
486+
{{- trace.path -}}
487+
{%- endif %} = {{ trace.value -}}
488+
</pre>
489+
{% elseif trace.message is defined %}
490+
<strong>{{ trace.class }}</strong><br/>
491+
<pre>{{ trace.message }}</pre>
492+
{% else %}
493+
<pre>{{ trace }}</pre>
494+
{% endif %}
481495
{% else %}
482-
<pre>{{ error.cause }}</pre>
483-
{% endif %}
496+
<em>Unknown.</em>
497+
{% endfor %}
484498
</td>
485499
</tr>
486500
{% endfor %}

src/Symfony/Component/Form/Button.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Form\Exception\AlreadySubmittedException;
1515
use Symfony\Component\Form\Exception\BadMethodCallException;
16+
use Symfony\Component\Form\Exception\TransformationFailedException;
1617

1718
/**
1819
* A form button.
@@ -344,6 +345,15 @@ public function isSynchronized()
344345
return true;
345346
}
346347

348+
/**
349+
* Unsupported method.
350+
*
351+
* @return null Always returns null
352+
*/
353+
public function getTransformationFailure()
354+
{
355+
}
356+
347357
/**
348358
* Unsupported method.
349359
*

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ CHANGELOG
2121
* ObjectChoiceList now compares choices by their value, if a value path is
2222
given
2323
* you can now pass interface names in the "data_class" option
24+
* [BC BREAK] added `FormInterface::getTransformationFailure()`
2425

2526
2.4.0
2627
-----

src/Symfony/Component/Form/Extension/DataCollector/FormDataExtractor.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,39 @@ public function extractSubmittedData(FormInterface $form)
115115
'origin' => is_object($error->getOrigin())
116116
? spl_object_hash($error->getOrigin())
117117
: null,
118+
'trace' => array(),
118119
);
119120

120121
$cause = $error->getCause();
121122

122-
if ($cause instanceof ConstraintViolationInterface) {
123-
$errorData['cause'] = array(
124-
'root' => $this->valueExporter->exportValue($cause->getRoot()),
125-
'path' => $this->valueExporter->exportValue($cause->getPropertyPath()),
126-
'value' => $this->valueExporter->exportValue($cause->getInvalidValue()),
127-
);
128-
} else {
129-
$errorData['cause'] = null !== $cause ? $this->valueExporter->exportValue($cause) : null;
123+
while (null !== $cause) {
124+
if ($cause instanceof ConstraintViolationInterface) {
125+
$errorData['trace'][] = array(
126+
'class' => $this->valueExporter->exportValue(get_class($cause)),
127+
'root' => $this->valueExporter->exportValue($cause->getRoot()),
128+
'path' => $this->valueExporter->exportValue($cause->getPropertyPath()),
129+
'value' => $this->valueExporter->exportValue($cause->getInvalidValue()),
130+
);
131+
132+
$cause = method_exists($cause, 'getCause') ? $cause->getCause() : null;
133+
134+
continue;
135+
}
136+
137+
if ($cause instanceof \Exception) {
138+
$errorData['trace'][] = array(
139+
'class' => $this->valueExporter->exportValue(get_class($cause)),
140+
'message' => $this->valueExporter->exportValue($cause->getMessage()),
141+
);
142+
143+
$cause = $cause->getPrevious();
144+
145+
continue;
146+
}
147+
148+
$errorData['trace'][] = $cause;
149+
150+
break;
130151
}
131152

132153
$data['errors'][] = $errorData;

src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public function validate($form, Constraint $constraint)
103103
->setParameters(array_replace(array('{{ value }}' => $clientDataAsString), $config->getOption('invalid_message_parameters')))
104104
->setInvalidValue($form->getViewData())
105105
->setCode(Form::ERR_INVALID)
106+
->setCause($form->getTransformationFailure())
106107
->addViolation();
107108
}
108109
}

src/Symfony/Component/Form/Form.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,10 @@ class Form implements \IteratorAggregate, FormInterface
121121
private $extraData = array();
122122

123123
/**
124-
* Whether the data in model, normalized and view format is
125-
* synchronized. Data may not be synchronized if transformation errors
126-
* occur.
127-
* @var bool
124+
* Returns the transformation failure generated during submission, if any
125+
* @var TransformationFailedException|null
128126
*/
129-
private $synchronized = true;
127+
private $transformationFailure;
130128

131129
/**
132130
* Whether the form's data has been initialized.
@@ -634,7 +632,7 @@ public function submit($submittedData, $clearMissing = true)
634632
$viewData = $this->normToView($normData);
635633
}
636634
} catch (TransformationFailedException $e) {
637-
$this->synchronized = false;
635+
$this->transformationFailure = $e;
638636

639637
// If $viewData was not yet set, set it to $submittedData so that
640638
// the erroneous data is accessible on the form.
@@ -711,7 +709,15 @@ public function isBound()
711709
*/
712710
public function isSynchronized()
713711
{
714-
return $this->synchronized;
712+
return null === $this->transformationFailure;
713+
}
714+
715+
/**
716+
* {@inheritdoc}
717+
*/
718+
public function getTransformationFailure()
719+
{
720+
return $this->transformationFailure;
715721
}
716722

717723
/**

src/Symfony/Component/Form/FormInterface.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Form;
1313

14+
use Symfony\Component\Form\Exception\TransformationFailedException;
15+
1416
/**
1517
* A form group bundling multiple forms in a hierarchical structure.
1618
*
@@ -230,10 +232,20 @@ public function isEmpty();
230232
/**
231233
* Returns whether the data in the different formats is synchronized.
232234
*
235+
* If the data is not synchronized, you can get the transformation failure
236+
* by calling {@link getTransformationFailure()}.
237+
*
233238
* @return bool
234239
*/
235240
public function isSynchronized();
236241

242+
/**
243+
* Returns the data transformation failure, if any.
244+
*
245+
* @return TransformationFailedException|null The transformation failure
246+
*/
247+
public function getTransformationFailure();
248+
237249
/**
238250
* Initializes the form tree.
239251
*

0 commit comments

Comments
 (0)