Anth Bieb
Anth Bieb

Reputation: 395

Symfony 2 - Adding error to form element on preSubmit event subscriber

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

Answers (3)

Will B.
Will B.

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

Francesco Abeni
Francesco Abeni

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

user2268997
user2268997

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

Related Questions