New in Symfony 5.3: Form Handler Helper

Symfony 5.3 is backed by:
Warning: This post is about an unsupported Symfony version. Some of this information may be out of date. Read the most recent Symfony Docs.
Contributed by
Grégoire Pineau
and Nicolas Grekas
in #41178
and #41190.
The recommended way of processing Symfony forms is to use a single action for both rendering the form and handling the form submit.
This is how it looks in practice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// src/Controller/ConferenceController.php
// ...
#[Route('/{id}/edit', name: 'conference_edit', methods: ['GET', 'POST'])]
public function edit(Request $request, Conference $conference): Response
{
$form = $this->createForm(ConferenceType::class, $conference);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// do something with the $conference object
// (e.g. persist it in the database)
return $this->redirectToRoute('conference_show', [
'id' => $conference->getId(),
]);
}
return $this->render('conference/edit.html.twig', [
'form' => $form->createView(),
]);
}
When using libraries such as Symfony UX Turbo this simple form handling is not enough and you have to follow the HTTP protocol strictly (e.g. if the form is submitted but invalid, the response must have a HTTP 422 status code).
In order to simplify the form handling in those cases, Symfony 5.3 adds a
new (optional) helper to render forms. This helper is defined in the
AbstractController base controller as a new method called renderForm()
.
This is how the last lines of the previous example should be written when using the new helper:
1 2 3 4 5 6 7
// src/Controller/ConferenceController.php
// ...
return $this->renderForm('conference/edit.html.twig', [
'form' => $form,
]);
}
The signature of the renderForm()
method is the same as for render()
:
1 2 3 4 5
renderForm(
string $view,
array $parameters = [],
Response $response = null
): Response
This method renders the given form (it calls $form->createView()
internally)
and sets the 422
status code when the form is submitted and invalid.
The $parameters
argument is the list of variables passed to the Twig
template and the optional $response
object allows you to configure certain
properties of the returned response (e.g. its cache options).
Help the Symfony project!
As with any Open-Source project, contributing code or documentation is the most common way to help, but we also have a wide range of sponsoring opportunities.
Comments
Comments are closed.
To ensure that comments stay relevant, they are closed for old posts.
Or even better: Form::getHttpCode() which will return the recommended HTTP code of the response. 303 on success if method is post, 200 if not submitted, 422 if submitted with error... then just $this->render(..., $form->getHttpCode()); // :)
I like this better than the old way of rendering forms, which my brain never managed to wrap around. In my mind the workflow is 1. render form, 2. handle submit and render response (either valid or invalid). The fact that "invalid response" looks generally very similar to the pristine form is of accidental nature and not core to the design of the API.
I would see as improvement anything that makes handling of invalid forms more explicit, such as the suggestions from Josef Kufner, or maybe adding to the signature of `handleForm` an optional callable $onIvalid, eg: `handleForm(FormInterface $form, Request $request, callable $render, callable $onSuccess, callable $onInvalid = null)`
protected function renderForm(string $template, FormInterface $form, array $params = []): Response
{
$code = $form->isSubmitted() && !$form->isValid() ? Response::HTTP_UNPROCESSABLE_ENTITY : Response::HTTP_OK;
return $this->render($template, array_merge($params, ['form' => $form->createView()]), new Response(null, $code));
}
or see this gist for a nicer view https://gist.github.com/garak/fbc48d46e5c6226fe0fe19597f5a5293
@Massimiliano Arione we did something similar initially, but this triggers the validation logic two times, and that hurts performance. Also, it makes it harder to pass custom arguments to the view, and to hook custom logic.
If it is interesting, I developed a bundle which works with the same principle and which allows to decouple the logic after submission of the form in a handler instead of leaving it in the controller. So the controller only does its job: to receive a request and send back a response.
See it here : https://github.com/Digivia/form-handler
https://github.com/symfony/symfony/pull/41178