[FrameworkBundle] Add AbstractController::handleForm() helper

This commit is contained in:
Kévin Dunglas 2021-04-13 14:52:55 +02:00 committed by Nicolas Grekas
parent dd919a7233
commit 5228546066
3 changed files with 68 additions and 36 deletions

View File

@ -7,7 +7,7 @@ CHANGELOG
* Deprecate the `session.storage` alias and `session.storage.*` services, use the `session.storage.factory` alias and `session.storage.factory.*` services instead
* Deprecate the `framework.session.storage_id` configuration option, use the `framework.session.storage_factory_id` configuration option instead
* Deprecate the `session` service and the `SessionInterface` alias, use the `Request::getSession()` or the new `RequestStack::getSession()` methods instead
* Added `AbstractController::renderForm()` to render a form and set the appropriate HTTP status code
* Added `AbstractController::handleForm()` to handle a form and set the appropriate HTTP status code
* Added support for configuring PHP error level to log levels
* Added the `dispatcher` option to `debug:event-dispatcher`
* Added the `event_dispatcher.dispatcher` tag

View File

@ -291,15 +291,28 @@ abstract class AbstractController implements ServiceSubscriberInterface
}
/**
* Renders a form.
* Handles a form.
*
* The FormView instance is passed to the template in a variable named "form".
* If the form is invalid, a 422 status code is returned.
* * if the form is not submitted, $render is called
* * if the form is submitted but invalid, $render is called and a 422 HTTP status code is set if the current status hasn't been customized
* * if the form is submitted and valid, $onSuccess is called, usually this method saves the data and returns a 303 HTTP redirection
*
* @param callable(FormInterface, mixed): Response $onSuccess
* @param callable(FormInterface, mixed): Response $render
*/
public function renderForm(string $view, FormInterface $form, array $parameters = [], Response $response = null): Response
public function handleForm(FormInterface $form, Request $request, callable $onSuccess, callable $render): Response
{
$response = $this->render($view, ['form' => $form->createView()] + $parameters, $response);
if ($form->isSubmitted() && !$form->isValid()) {
$form->handleRequest($request);
$submitted = $form->isSubmitted();
$data = $form->getData();
if ($submitted && $form->isValid()) {
return $onSuccess($form, $data);
}
$response = $render($form, $data);
if ($submitted && 200 === $response->getStatusCode()) {
$response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
}

View File

@ -23,7 +23,6 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormConfigInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File;
@ -425,50 +424,70 @@ class AbstractControllerTest extends TestCase
$this->assertInstanceOf(StreamedResponse::class, $controller->stream('foo'));
}
public function testRenderFormTwig()
public function testHandleFormNotSubmitted()
{
$formView = new FormView();
$form = $this->createMock(FormInterface::class);
$form->expects($this->once())->method('createView')->willReturn($formView);
$twig = $this->createMock(Environment::class);
$twig->expects($this->once())->method('render')->with('foo', ['form' => $formView, 'bar' => 'bar'])->willReturn('bar');
$container = new Container();
$container->set('twig', $twig);
$form->expects($this->once())->method('isSubmitted')->willReturn(false);
$controller = $this->createController();
$controller->setContainer($container);
$response = $controller->renderForm('foo', $form, ['bar' => 'bar']);
$response = $controller->handleForm(
$form,
Request::create('https://example.com'),
function (FormInterface $form, $data): Response {
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
},
function (FormInterface $form, $data): Response {
return new Response('rendered');
}
);
$this->assertTrue($response->isSuccessful());
$this->assertSame('bar', $response->getContent());
$this->assertSame('rendered', $response->getContent());
}
public function testRenderInvalidFormTwig()
public function testHandleFormInvalid()
{
$formView = new FormView();
$form = $this->createMock(FormInterface::class);
$form->expects($this->once())->method('createView')->willReturn($formView);
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
$form->expects($this->once())->method('isValid')->willReturn(false);
$twig = $this->createMock(Environment::class);
$twig->expects($this->once())->method('render')->with('foo', ['form' => $formView, 'bar' => 'bar'])->willReturn('bar');
$container = new Container();
$container->set('twig', $twig);
$controller = $this->createController();
$controller->setContainer($container);
$response = $controller->renderForm('foo', $form, ['bar' => 'bar']);
$response = $controller->handleForm(
$form,
Request::create('https://example.com'),
function (FormInterface $form): Response {
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
},
function (FormInterface $form): Response {
return new Response('rendered');
}
);
$this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode());
$this->assertSame('bar', $response->getContent());
$this->assertSame('rendered', $response->getContent());
}
public function testHandleFormValid()
{
$form = $this->createMock(FormInterface::class);
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
$form->expects($this->once())->method('isValid')->willReturn(true);
$controller = $this->createController();
$response = $controller->handleForm(
$form,
Request::create('https://example.com'),
function (FormInterface $form): Response {
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
},
function (FormInterface $form): Response {
return new Response('rendered');
}
);
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertSame(Response::HTTP_SEE_OTHER, $response->getStatusCode());
$this->assertSame('https://example.com/redir', $response->getTargetUrl());
}
public function testRedirectToRoute()