Clément Cartier
Clément Cartier

Reputation: 185

Symfony 5 : handling multiple forms in one controller

I am trying to manage multiple forms in the same page in Symfony 5 with the following function, but it seems that every time I try to submit a form, only the first form of the list is handled even if it is not the one that has been submitted:

class ContentController extends AbstractController
{
    /**
     * @Route("/category/edition/{content}", name="edit_category")
     */
    public function edition(Request $request, Category $content): Response
    {
        $forms = [
            "edit_category" => $this->createForm(EditCategoryType::class, $content),
            "create_post" => $this->createForm(CreatePostType::class)
        ];
        foreach($forms as $form) {
            $form->handleRequest($request);
            if($form->isSubmitted() && $form->isValid()) {
                // Always prints edit_category
                // even when that is the the create_post that is submitted
                return var_dump($form->getName());
            }
        }
        return $this->render(
            'content/edition.html.twig',
            [
                'forms' => \array_map(
                    function($form) {
                        return $form->createView();
                    },
                    $forms
                ),
                'content' => $content,
            ]
        );
    }
}

I have seen in other posts that the name of the forms can sometime raise an issue, but I have checked that the forms do have different names, and I have also tried to call handleRequest() on every form in a separate foreach loop because I have seen this done in some posts, but it (quite expectedly I must say) didn't change the behavior.

And I didn't seem to find any unanimous best practice tips about how to handle multiple forms in the same Controller in Symfony, so I was wondering what is the best way to do it, or if it would be cleaner to define a separate action route for each form in order to avoid this problem altogether.

If needed, the content/edition.html.twig file looks something like that:

{% set edit_category_form = forms['edit_category'] %}
{% set create_post_form = forms['create_post'] %}
{{ form_start(edit_category_form) }}
    {{ form_errors(edit_category_form) }}
{{ form_end(edit_category_form) }}
{{ form_start(create_post_form) }}
    {{ form_errors(create_post_form) }}
{{ form_end(create_post_form) }}

(Category is a classical Symfony entity, EditCategoryType is a form associated with the Category entity and CreatePostType is a form associated with another Symfony entity)

Upvotes: 1

Views: 2314

Answers (1)

Clément Cartier
Clément Cartier

Reputation: 185

After some research, it seems for some reason like it works if (and only if?) the form is build just before handling the request:

class ContentController extends AbstractController
{
    /**
     * @Route("/category/edition/{content}", name="edit_category")
     */
    public function edition(Request $request, Category $content): Response
    {
        $self = $this;
        $formBuilders = [
            "edit_category" => function() use ($self, $content) {
                return $self->createForm(EditCategoryType::class, $content);
            },
            "create_post" => function() use ($self, $content) {
                return $self->createForm(CreatePostType::class);
            },
        ];
        $forms = [];
        foreach($formBuilders as $key => $formBuilder) {
            $form = $formBuilder();
            $forms[$key] = $form;
            $form->handleRequest($request);
            if($form->isSubmitted() && $form->isValid()) {
                // Does print the name of the right form
                return var_dump($form->getName());
            }
        }
        return $this->render(
            'content/edition.html.twig',
            [
                'forms' => \array_map(
                    function($form) {
                        return $form->createView();
                    },
                    $forms
                ),
                'content' => $content,
            ]
        );
    }
}

It works but it doesn't feel like a proper way to handle this !

Upvotes: 2

Related Questions