Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

symfony2 : form errors not displayed in twig despite a non null getErrorsAsString()

I am using symfony 2 and I have a form on which I put @Assert\NotBlank() annotations. I am volontarily filling the fields, my form does not pass the isValid and isSubmitted test and after those lines I get a non-null value for exit(var_dump($recipeForm->getErrors()));

  private 'errors' => 
    array (size=4)
      0 => 
        object(Symfony\Component\Form\FormError)[4119]
          private 'message' => string 'Cette valeur doit être vide.' (length=29)
          protected 'messageTemplate' => string 'This value should be blank.' (length=27)
          protected 'messageParameters' => 
            array (size=1)
              ...
          protected 'messagePluralization' => null
          private 'cause' => 
            object(Symfony\Component\Validator\ConstraintViolation)[4062]
              ...
          private 'origin' => null

In my twig template I render the form with a simple form(form). form_errors(form) or form_errors(form.field) won't render the errors. Why? Why could I do to further understand where the issue comes from?

I have a pretty complicated type. for some other forms of my website, the errors are correctly displayed. My type:

<?php
//src/AppBundle/Form/FoodAnalytics/RecipeType.php
namespace AppBundle\Form\FoodAnalytics;

use AppBundle\Form\Core\MediaType;
use AppBundle\Repository\FoodAnalytics\UnitRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RecipeType extends AbstractType
{
    protected $recipeIngredientQueryBuilder;
    protected $recipeSubrecipeQueryBuilder;
    protected $unitRepository;
    protected $action;

    /**
     * @return string
     */
    public function getName()
    {
        return 'appbundle_foodanalytics_recipe' . $this->action;
    }

    public function __construct(UnitRepository $unitRepository, $recipeIngredientQueryBuilder=null, $recipeSubrecipeQueryBuilder=null, $action = null)
    {
        $this->recipeIngredientQueryBuilder = $recipeIngredientQueryBuilder;
        $this->recipeSubrecipeQueryBuilder = $recipeSubrecipeQueryBuilder;
        $this->unitRepository = $unitRepository;
        $this->action = $action == null ? null : '_' . $action;
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('recipeCategories', 'genemu_jqueryselect2_entity',array(
                    'multiple' => true,
                    'class' => 'AppBundle:FoodAnalytics\RecipeCategory',
                    'label' => 'Catégories',
                    'required' => false,
                    'by_reference' => false,
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Indiquez les catégories dans lesquelles enregistrer la recette pour un recherche future plus facile",
                    )))
            ->add('isProduct', null, array(
                    'label'=>'Correspond à un produit fini',
                    'required'=>false,
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"La recette correspond-elle à un produit fini qui peut être mis en vente ?",
                    )))
            ->add('name', null, array(
                    'label'=>'Nom détaillé',
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Indiquez le nom détaillé de la recette. Par exemple : 'millefeuilles praliné ganache vanille sur feuilletage inversé'",
                    )))
            ->add('nickName', null, array(
                    'label'=>'Nom raccourci',
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Indiquez un nom raccourci pour la recette. Par exemple : 'millefeuilles'",
                    )))
            ->add('recipeIngredients', 'collection', array(
                    'type' => new RecipeIngredientType($this->unitRepository, $this->recipeIngredientQueryBuilder),
                    'by_reference' => false,
                    'label'=>'Ingrédient',
                    'allow_add' => true,
                    'allow_delete' => true,
                    'cascade_validation' => true,
                ))
            ->add('subrecipes', 'collection', array(
                    'type' => new RecipeSubrecipeType($this->unitRepository, $this->recipeSubrecipeQueryBuilder),
                    'by_reference' => false,
                    'label'=>'Sous-recette',
                    'allow_add' => true,
                    'allow_delete' => true
                ))
            ->add('recipeSteps', 'collection', array(
                    'type' => new RecipeStepType(),
                    'by_reference' => false,
                    'label'=>'Etape de production',
                    'allow_add' => true,
                    'allow_delete' => true
                ))
            ->add('portions', null, array(
                    'label'=>'Nombre de parts / de pièces',
                    'required' => false,
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Indiquez le nombre d'éléments disponibles dans la recette. Cela peut permettre d'utiliser l'unité 'U' dans les recettes parentes qui l'utiliseront",
                    )))
            ->add('shortDescription', null, array(
                    'label'=>'Description courte',
                    'required'=>false,
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Décrivez succinctement la recette",
                    )))
            ->add('medias', 'collection', array(
                    'type' => new MediaType(),
                    'by_reference' => false,
                    'label'=>'Medias',
                    'allow_add' => true,
                    'allow_delete' => true,
                    'required' => false,
                    'attr'=>array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'title'=>"Ajoutez des images ou vidéos pour décrire la recette",
                    )))
            ->add('content', 'textarea', array(
                    'label'=>'Instructions générales',
                    'required'=>false,
                    'attr' => array(
                        'data-toggle'=>"tooltip",
                        'data-placement'=>"top",
                        'class' => 'summernote',
                        'title'=>"Ajoutez du contenu supplémentaire pour détailler la recette",
                    )))
            ->add('workingDuration', 'timepicker', array(
                        'label'=>'Temps total de travail',
                        'required' => false,
                        'attr'=>array
                        (
                            'class' => 'timepicker',
                            'data-toggle'=>"tooltip",
                            'data-placement'=>"top",
                            'title'=>"Indiquez le temps total de travail consacré à la recette si il diffère du temps de travail cumulé des étapes de production",
                        )))
            ->add('sleepDuration', 'timepicker', array(
                    'label'=>'Temps total de repos',
                    'required' => false,
                    'attr'=>array
                    (
                        'data-toggle'=>"tooltip",
                        'class'=>'timepicker',
                        'data-placement'=>"top",
                        'title'=>"Indiquez le temps total de repos consacré à la recette si il diffère du temps de repos cumulé des étapes de production",
                    )))
        ;
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\FoodAnalytics\Recipe',
//            'cascade_validation' => true,
        ));
    }
}

EDIT: keeping only one simple field of this form does not change anything, the error won't get shown

like image 986
Sébastien Avatar asked Dec 23 '14 11:12

Sébastien


People also ask

How do I handle form errors in twig?

Normally, field errors are rendered in Twig by calling form_errors on each individual field: The form_row function calls form_errors internally. Just like in the controller, the errors are attached to the individual fields themselves.

How to get and display errors of forms in Symfony 3?

In this case, we'll show how to get and display the errors of forms in Symfony 3. To list all the errors of a form in a twig view, you'll need to check first if it has errors checking for form.vars.valid property. Then, loop through every form children and print all the errors on it.

What is form_row () in Symfony?

Renders the "row" of a given field, which is the combination of the field's label, errors, help and widget. The second argument to form_row () is an array of variables. The templates provided in Symfony only allow to override the label as shown in the example above.

What is the difference between form_errors and form_errors in twig?

Hopefully it make sense now why form_errors (form) renders global errors and form_errors (form.name) renders the errors attached to the name field. Once you're in Twig, each field (e.g. form, form.name) is an instance of :symfonyclass: Symfony\Component\Form\FormView.


2 Answers

Take a look at how errors are rendered here: http://symfony.com/doc/current/cookbook/form/form_customization.html#customizing-error-output

The problem with using {{ form_errors(form) }} is that it displays global form errors, not individual fields, whereas getErrorsAsString() will drill-down through all of the fields. If you want all of the individual field errors to be accessible via {{ form_errors(form) }}, then you need to edit every single field in your form and add the option error_bubbling => true.

If you don't set the error bubbling to true on all of our fields, then you will need to render each field's error individually - for example: {{ form_errors(form.name) }}, or just using {{ form_row(form.name) }} which renders the label, form element, and errors all in one shot.

like image 99
Jason Roman Avatar answered Oct 03 '22 21:10

Jason Roman


Ah my god!

I'm sorry i made people lose time with this! The issue was I was passing as a response an array $response = array(form->createView()) but this array response was generated before I handle the request. Hence why I could see it in the vardump and not in twig.

like image 32
Sébastien Avatar answered Oct 03 '22 19:10

Sébastien