An alternative approach leveraging Symfony, is to implement submission redirects on the form view controller to the form processor controller.
This works by receiving the form values once and redirecting the user immediately upon the receipt of the values.
So if the action is canceled and resubmitted or clicked multiple times, the initial request(s) fail to redirect the user to the form processor. Effectively preventing multiple submissions of data, since a redirect response is returned to the user and the form processor is not invoked.
The redirect using status code 307
acts as shortcut, allowing the entire request to be passed to another controller method and retain the request method type and data. Meanwhile the user won't visually notice a change and won't receive an invalid CSRF token error.
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DefaultController extends Controller
{
/**
* @Route('/submit', name="submit")
*/
public function submitAction(Request $request)
{
if ($request->isMethod($request::METHOD_POST)) {
return $this->redirectToRoute('process', ['request' => $request], 307);
}
//... form view
$form = $this->createForm(FormType::class, $data, ['action' => $this->generateUrl('submit')]);
$form->handleRequest($request);
/**
* alternative to comparing the request method type above
* if ($form->isSubmitted()) {
* return $this->redirectToRoute('process', ['request' => $request], 307);
* }
*/
return $this->render('::form_view', [
'form' => $form->createView()
]);
}
/**
* @Route('/process', name="process")
*/
public function processAction(Request $request)
{
$form = $this->createForm(FormType::class, $data, ['action' => $this->generateUrl('submit')]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//... process form
$this->addFlash('success', 'Form Processed!');
return $this->redirectToRoute('submit');
}
//... show form errors and allow resubmission
return $this->render('::form_view', [
'form' => $form->createView()
]);
}
}