Symfony2: my form returns false from isValid() but empty array for getErrors() from unique constraint condition

34,876

Solution 1

Ok, found an answer here:

Symfony2 invalid form without errors

It turns out each form child has it's own separate errors. When doing a var_dump of

$editForm->getChildren()['email']->getErrors()

I get:

array (size=1)
  0 => 
    object(Symfony\Component\Form\FormError)[531]
      private 'message' => string 'A customer under that email address already exists' (length=50)
      protected 'messageTemplate' => string 'A customer under that email address already exists' (length=50)
      protected 'messageParameters' => 
        array (size=0)
          empty
      protected 'messagePluralization' => null

I am still wondering how to determine that the error is because of a unique conflict without parsing the error message string.

Solution 2

For debug purposes you can use $form->getErrorsAsString() instead of $form->getErrors() if you use Symfony 2.*

Quoted from this answer:

$form->getErrorsAsString() should only be used to debug the form...it will contain the errors of each child elements which is not the case of $form->getErrors().


UPDATE 1:

"With more recent Symfony versions, you must use $form->getErrors(true, false); instead. First param corresponds to deep and second to flatten" (see the comment by @Roubi)

Solution 3

The following solutions works for me:

$form->getErrors(true)

Solution 4

You can use error_bubbling on each field to bubble the error up to your $form.

If not, you can also foreach through the errors

foreach ($children as $child) {
            if ($child->hasErrors()) {
                $vars = $child->createView()->getVars();
                $errors = $child->getErrors();
                foreach ($errors as $error) {
                    $this->allErrors[$vars["name"]][] = $this->convertFormErrorObjToString($error);
                }
            }
}
Share:
34,876
Boris_bulletdodger
Author by

Boris_bulletdodger

Updated on November 05, 2020

Comments

  • Boris_bulletdodger
    Boris_bulletdodger over 3 years

    I have a Customer entity that only has a unique Email field to it. I am trying to edit a customer's email and the validation works fine. However I have this in my controller:

    public function updateAction(Request $request, $id) {
        $em = $this->getDoctrine()->getManager();
    
        $entity = $em->getRepository('AcmeDemoBundle:Customer')->find($id);
    
        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Customer entity.');
        }
    
    
        $editForm = $this->createForm(new CustomerType(), $entity);
        $editForm->bind($request);
        if ($editForm->isValid()) {
            $em->persist($entity);
            $em->flush();
    
            return $this->redirect($this->generateUrl('ticket_result'));
        }
        var_dump($editForm->getErrors());
    
        return $this->render('AcmeDemoBundle:Customer:edit.html.twig', array(
                    'entity' => $entity,
                    'edit_form' => $editForm->createView(),
        ));
    }
    

    The var_dump returns an empty array but the validator sets a unique error and the $editForm->isValid() returns false. Is there a way to check for that specific error in the controller during validation, also can you explain why it returns an empty error array? Basically, I would like to provide the "merge" option if that error comes up.

    EDIT: here is the formtype:

    namespace Acme\DemoBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
    
    class CustomerType extends AbstractType {
    
    
        public function buildForm(FormBuilderInterface $builder, array $options) {
            $builder
                    ->add('email', 'email', array('required'=>true))
            ;
    
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'data_class' => 'Acme\DemoBundle\Entity\Customer',
                'cascade_validation' => true,
            ));
        }
    
        public function getName() {
            return 'acme_demobundle_customertype';
        }
    }
    

    And the twig template:

    {% extends 'AcmeDemoBundle::layout.html.twig' %}
    {% block body -%}
        <h1>Customer edit</h1>
    
    
    
      <form action="{{ path('customer_update', { 'id': entity.id }) }}" method="post" {{ form_enctype(edit_form) }}>
            <input type="hidden" name="_method" value="PUT" />
            {{ form_widget(edit_form) }}
            <p>
                <button type="submit">Edit</button>
            </p>
        </form>
    
    {% endblock %}
    

    Here is my validation:

    Acme\DemoBundle\Entity\Customer:
        constraints:
          - Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity: 
              fields: email
              message: "A customer under that email address already exists"
    
        properties:
            email:
                - Email: ~
    
  • Jean-Christophe Meillaud
    Jean-Christophe Meillaud almost 9 years
    getErrorsAsString is Deprecated since version 2.5, to be removed in 3.0. Use getErrors()} instead and cast the result to a string.
  • contrebis
    contrebis over 8 years
    $editForm->getErrors(true) will now return form errors from children recursively (since 2.5).
  • ChristoKiwi
    ChristoKiwi over 8 years
    @DénesPapp pass the optional Boolean param to cascade through any child forms $form->getErrors(true);
  • Roubi
    Roubi over 8 years
    With more recent Symfony versions, you must use $form->getErrors(true, false); instead. First param corresponds to deep and second to flatten