5
votes

I got a problem with a dynamic form on symfony2. I'm trying to generate some fields for a submitted form. In others words, the user enters some values, submits the form, and according to these values, my dynamics fields are added to this same form (which is, obviously, displayed a second time). To do that, I used this example from the cookbook : http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

So, here is my FormationType class

class FormationType extends AbstractType
{

private $em;
private $context;

public function __construct($em, $context) {
    $this->em = $em;
    $this->context = $context;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name')
        ->add('date')
        ->add('type', 'choice', array(
            'mapped' => false,
            'choices' => Formationlist::getTypeTypes(false),
            'empty_value' => false,
        ))
        ->add('cost')
        ->add('travelCost')
        ->add('maximum')
        ->add('location')
        ->add('schedule')
    ;

    $formModifier = function(FormInterface $form, $type) {
        $formationList = $this->em->getRepository('CoreBundle:FormationList')->findBy(array("year" => 1, "type" => $type));

        $form->add('formationList', 'entity', array(
            'label'=> 'Titre formation', 
            'choices' => $formationList, 
            'class' => 'CoreBundle:FormationList', 
            'property' => 'title',)
                );

        };


    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function(FormEvent $event) use ($formModifier) {

            $data = $event->getForm();
            $type = $data->get('type')->getData();

            $formModifier($event->getForm(), $type);

        }
    );

    $builder->get('type')->addEventListener(
        FormEvents::POST_SUBMIT,
        function(FormEvent $event) use ($formModifier) {

            $type = $event->getForm()->getData();
            $formModifier($event->getForm()->getParent(), $type);
        }
    );
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'EXAMPLE\CoreBundle\Entity\Formation'
    ));
}

public function getName()
{
    return 'example_corebundle_formationtype';
}
}

So, the two addEventListener work pretty well. The first time my form is displayed, the field in formModifier is not loaded, as expected. My controller class is the following one :

public function createAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $contextSrv = $this->get('example.service.context');
    $context = $contextSrv->getContext();

    $entity  = new Formation();
    $form = $this->createForm(new FormationType($em, $context), $entity);
    $form->bind($request);

    if ($form->isValid()) {

        $em->persist($entity);
        $em->flush();

        return $this->redirect($this->generateUrl('formation_show', array('id' => $entity->getId())));
    }

    return array(
        'entity' => $entity,
        'form'   => $form->createView(),
    );
}

Since one of my dynamic field can't be null, the first time the form is submitted, it can't be valid. So, the FormationType is loaded a second time. That means, if the field "type" was filled, my formModifier() function can load the dynamic field (formationList). Until there, everything works pretty well, and I got my new field.

But, after a second "submit" on the form...nothing happen. The page is just reloaded, and no errors are displayed.

I checked the form content with

var_dump($request->request->get('example_corebundle_formationtype'));

-> Every fields (including the dynamic one) are filled with valid values.

I also try this :

foreach($form->all() as $item) {
       echo $item->getName();
        var_dump($item->getErrors());
}

-> These lines don't show any error. But, the form is never valid.

var_dump($form->isValid());

-> It returns false. So the form is invalid.

Finally, if I remove the whole dynamic part, my form works. I don't understand what's wrong. There is no errors displayed by the form, and the csrf token seems right. Did I miss something ? Thanks for your help.

3
Just a guess, can you check with firebug if the value for the <input> type is the same when you delete this line of code: 'choices' => $formationList, from your form $formModifier? - Cesc
hm...i don't understand your point. I removed 'choices' => $formationList, from $formModifier, but nothing happend to the field "type". Which is expecting since 'type' is a select without dynamic value. The formationList field was, and is still a list (the number of <option> is the only difference). Thanks for your anwser ! - TiPi
inside the <option> </option> should be a valid id (whatever primary key you specified) for the class FormationList, if not, $form->isValid() will be false. That was my point, but I am quite sure the list $formationList is well done and therefore it should have proper id. It was just a wild guess. - Cesc
Add a {{ form_errors(form) }} to print all form errors on the view. Maybe you have some validator on a entity function? - albert
It seems that error bubbling is disabled. have you tried setting error_bubbling => true? Also, you are referring to a 2.4 documentation. Are you using Symfony 2.4.x? If so, the Profiler has a Form section. It can be useful to trace what happens. - Dovydas Bartkevičius

3 Answers

1
votes

There is probably a validation error lying somewhere in your form.

Instead of your complicated calls to Form::getErrors() - which is not fully recursive, as the errors of any field deeper than the 2nd level will not be displayed - you should use Form::getErrorsAsString().

This is a debug method created by the Symfony guys for developers such as you, trying to understand where a validation error could lie in complex forms.

If no error is displayed although it should, this may be a form theming error. When creating a custom form theme, it is possible that a developper overrides or forgets to display the error block of a field.

Another possible source of the problem lies is the general display of the form. If you display your form using {{ form_widget(form) }}, then any error that bubbles to the top form will never be displayed. Make then sure that you use {{ form_row(form) }} instead.

1
votes

I know this is a bit outdated but comes up quite high on Google.

The getErrors() metod returns only Form's global errors not error messages for the underlying fields, you need either getErrors(true) or more sophisticated method when using embeded forms in a form. Please see: https://knpuniversity.com/blog/symfony-debugging-form-errors for more information.

0
votes

I also encountered this problem a few times.

In my case I posted data in JSON format, so I had to do a request listener with a high priority which transforms json data into normal POST data, which is available in $request->request.

One scenario where the $form is invalid and there is no errors in also when the post data is empty, try to make a dump of $request->request->all() to see if you have the data.