Reputation: 395
I have a preSubmit event in an event subscriber for a form, and for a specific case I want to add an error to a form field. My method within the subscriber is as follows:
public function onPreSubmit(FormEvent $event)
{
$sourceData = $event->getData();
$form = $event->getForm();
$identifier = &$sourceData['identifier'];
if ($identifier) {
if ($this->identifierIsUrl($identifier)) {
$parser = $this->getIdParser();
$identifier = $parser->getIdentifier($identifier);
if (is_null($identifier)) {
$form->get('identifier')->addError(new FormError('You have either entered an incorrect url for the source or it could not be parsed'));
}
}
$event->setData($sourceData);
}
}
However when I print the form error in the view, it is empty. Is it possible to do this in a preSubmit event? Am I looking at this the wrong way?
Upvotes: 3
Views: 7107
Reputation: 18416
The issue is related to the Symfony\Component\Form\Form::submit
method that removes all of the form field specific errors assigned after the PRE_SUBMIT
event.
During Form::submit
it iterates over all the forms child objects (which are also themselves Form
objects as noted by the other answers) and calls their submit
methods individually. Resulting in the form element errors that were added during the parent's PRE_SUBMIT
event to be reset to an empty array.
This is why you can use $form->addError()
in the parent PRE_SUBMIT
event or set the form element to error_bubbling => true
and it will display as the parent form errors, but not to a specific form element.
Here's the example of what occurs without looking through the entire codebase for Symfony Forms.
class Form
{
public function addError($error)
{
if($this->parent && $this->config->getErrorBubbling()) {
$this->parent->addError($error); //element had error_bubbling => true, attach the error to the parent.
} else {
$this->errors[] = $error; //add it to the current object's errors array
}
}
public function submit()
{
$this->errors = array(); //resets the errors of the current object
$this->preSubmitEvent();
foreach($this->children as $child) {
$child->submit(); //errors in child object are reset
}
}
}
So it results in
Form:
submitMethod:
preSubmitEvent
children:
submitMethod:
preSubmitEvent
To get around the issue you can add a PRE_SUBMIT
event directly to your form element to validate that element and add errors to it.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('identifier', Form\TextType::class);
//...
$builder->get('identifier')->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'validateIdentifier']);
}
Then alter your onPreSubmit
method accordingly.
public function validateIdentifier(FormEvent $event)
{
$identifier = $event->getData();
$element = $event->getForm();
if ($identifier) {
if ($this->identifierIsUrl($identifier)) {
$parser = $this->getIdParser();
$identifier = $parser->getIdentifier($identifier);
if (null === $identifier) {
$element->addError(new FormError('You have either entered an incorrect url for the source or it could not be parsed'));
}
}
$event->setData($identifier);
}
}
Upvotes: 9
Reputation: 4265
It is possible that errors are just not displayed in your layout.
To debug, make sure your form is actually made invalid by your custom check (if you show your controller I may be able to help with actual code).
If that is in fact your issue, you may want to add error_bubbling => true
to your field definition.
Upvotes: 0
Reputation: 1391
It should be possible if you look at the Form::submit(), you'll see that the errors are reset before the PRE_SUBMIT
data is dispatched. Also Validation Listener doesn't reset the errors on the form, and the Violation Mapper only adds Errors to the form. And AFIK there aren't any listeners that reset the form's errors. So maybe you're doing something else wrong.
Upvotes: 0