-
Notifications
You must be signed in to change notification settings - Fork 15
Getting Started With Form Handlers
If you are familiar with the Symfony form component, these examples will seem very familiar, this is by design. Form handler types are designed to be symmetrical to form types in the way they are used and configured. To get started, it's a good idea to have a sense of what it looks like with and without form handlers. Form handlers will reduce the amount of code in controllers by moving it to a separate class.
In the following example, a user account would be updated and a log would be stored accordingly. This example isn't a fat controller, but even this controller can be trimmed down.
<?php
namespace App\Controller;
use App\Entity\Account;
use App\Entity\ActionLog;
use App\FormType\EditUserType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\User\UserInterface;
class EditUserController extends Controller
{
/**
* @Route("/edit-user/{id}/", name="app.edit-user")
*/
public function editUserAction(Request $request, Account $account, UserInterface $user)
{
$form = $this->createForm(EditUserType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$log = new ActionLog($user, $user->getUsername(), 'Updated the user account.');
$em = $this->getDoctrine()->getManager();
$em->persist($log);
$em->flush();
$this->addFlash('success', 'The user has been updated.');
return $this->redirectToRoute('app.user-list');
}
return $this->renderView('edit_user.html.twig', [
'form' => $form->createView(),
]);
}
}
Rewriting your code to work with form handlers, you require a new class which implements the Hostnet\Component\FormHandler\HandlerTypeInterface
. This class will implement a configure method, which is required to set the form type to use. Besides of that, you're free to configure it as you please.
In the given example, you only need to configure the form type and what should be done when the form is submitted and valid.
<?php
namespace App\FormHandler;
use App\FormType\EditUserType;
use Doctrine\ORM\EntityManagerInterface;
use Hostnet\Component\FormHandler\HandlerConfigInterface;
use Hostnet\Component\FormHandler\HandlerTypeInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
final class EditUserFormHandler implements HandlerTypeInterface
{
private $em;
private $token_storage;
private $flash_bag;
private $url_generator;
public function __construct(
EntityManagerInterface $em,
TokenStorageInterface $token_storage,
FlashBagInterface $flash_bag,
UrlGeneratorInterface $url_generator
) {
$this->em = $em;
$this->token_storage = $token_storage;
$this->flash_bag = $flash_bag;
$this->url_generator = $url_generator;
}
public function configure(HandlerConfigInterface $config)
{
// this will tell the form component to use this form type
// to generate the related form and configured data_class
$config->setType(EditUserType::class);
// Can be any type of callable. The arguments are always
// - The data object
// - The FormInterface built based on the type given above
// - The Request object used for the form
$config->onSuccess(function (Account $user) {
$by = $this->token_storage->getToken()->getUsername();
$log = new ActionLog($user, $by, 'Updated the user account.');
$this->em->persist($log);
$this->em->flush();
$this->flash_bag->add('success', 'The user has been updated.');
return new RedirectResponse($this->url_generator->generate('app.user-list'));
});
}
}
For more information on the
onSuccess
andonFailure
callbacks, see Using the onSuccess and onFailure.
To let the form handler bundle know about this handler, it has to be tagged with a form.handler
tag. As your class has dependencies, the service definition could look like this:
services:
app.form_handler.edit_username:
class: App\FormHandler\EditUserFormHandler
arguments:
- "@doctrine.orm.entity_manager"
- "@security.token_storage"
- "@session.flash_bag"
- "@router"
tags:
- { name: form.handler }
Now that is defined which actions should be taken upon a successful form submission with valid data, it can be removed from the controller.
<?php
namespace App\Controller;
use App\Entity\Account;
use App\FormHandler\EditUserFormHandler;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class EditUserController extends Controller
{
/**
* @Route("/edit-user/{id}/", name="app.edit-user")
*/
public function editUserAction(Request $request, Account $account)
{
$handler = $this->get('hostnet.form_handler.factory')->create(EditUserFormHandler::class);
if (($response = $handler->handle($request, $account)) instanceof RedirectResponse) {
return $response;
}
return $this->renderView('edit_user.html.twig', ['form' => $handler->getForm()->createView()]);
}
}