5

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.

6
  • 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? Commented Oct 21, 2013 at 6:48
  • 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 ! Commented Oct 23, 2013 at 8:12
  • 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. Commented Oct 23, 2013 at 9:12
  • Add a {{ form_errors(form) }} to print all form errors on the view. Maybe you have some validator on a entity function? Commented Nov 29, 2013 at 16:51
  • 1
    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. Commented Jan 9, 2014 at 6:14

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

3 Comments

Good to know, the getErrorsAsString, thanks for the tip ! Unfortunately, it didn't show any error. Only "No errors" for every single field... I also used row_form instead of form_widget for the same result. And since I don't use custom theme for this form...this shouldn't be in cause. But your advice will help me to find out what's wrong !
But that is absurd. If you check yourself the base definition of Form::isValid(), this method just recursively check for the existence of errors. If Form::getErrorsAsString() returns nothing, then isValid() should be true, period. Are you using a custom isValid() method? Are you absolutely sure that you make your checks and dumps after the form is bound?
It is also possible there is an extra field.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.