[FrameworkBundle] Introduce AbstractController::renderForm()
This commit is contained in:
parent
fef109932f
commit
27f8f50386
@ -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::handleForm()` to handle a form and set the appropriate HTTP status code
|
||||
* Added `AbstractController::renderForm()` to render 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
|
||||
|
@ -266,6 +266,24 @@ abstract class AbstractController implements ServiceSubscriberInterface
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a view for a form.
|
||||
*
|
||||
* The FormView instance is passed to the template in a variable named
|
||||
* "form" (can be changed via $formVar argument).
|
||||
* If the form is invalid, a 422 status code is returned.
|
||||
*/
|
||||
protected function renderForm(string $view, FormInterface $form, array $parameters = [], Response $response = null, string $formVar = 'form'): Response
|
||||
{
|
||||
$response = $this->render($view, [$formVar => $form->createView()] + $parameters, $response);
|
||||
|
||||
if ($form->isSubmitted() && !$form->isValid()) {
|
||||
$response->setStatusCode(422);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams a view.
|
||||
*/
|
||||
@ -290,42 +308,6 @@ abstract class AbstractController implements ServiceSubscriberInterface
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a form.
|
||||
*
|
||||
* * 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
|
||||
*
|
||||
* For both callables, instead of "mixed", you can use your form's data class as a type-hint for argument #2.
|
||||
*
|
||||
* @param callable(FormInterface, mixed, Request): Response $onSuccess
|
||||
* @param callable(FormInterface, mixed, Request): Response $render
|
||||
*/
|
||||
public function handleForm(FormInterface $form, Request $request, callable $onSuccess, callable $render): Response
|
||||
{
|
||||
$form->handleRequest($request);
|
||||
|
||||
$submitted = $form->isSubmitted();
|
||||
$data = $form->getData();
|
||||
|
||||
if ($isValid = $submitted && $form->isValid()) {
|
||||
$response = $onSuccess($form, $data, $request);
|
||||
} else {
|
||||
$response = $render($form, $data, $request);
|
||||
|
||||
if ($response instanceof Response && $submitted && 200 === $response->getStatusCode()) {
|
||||
$response->setStatusCode(Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
throw new \TypeError(sprintf('The "%s" callable passed to "%s::handleForm()" must return a Response, "%s" returned.', $isValid ? '$onSuccess' : '$render', get_debug_type($this), get_debug_type($response)));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a NotFoundHttpException.
|
||||
*
|
||||
|
@ -23,6 +23,7 @@ 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;
|
||||
@ -411,6 +412,52 @@ class AbstractControllerTest extends TestCase
|
||||
$this->assertEquals('bar', $controller->render('foo')->getContent());
|
||||
}
|
||||
|
||||
public function testRenderFormNew()
|
||||
{
|
||||
$formView = new FormView();
|
||||
|
||||
$form = $this->getMockBuilder(FormInterface::class)->getMock();
|
||||
$form->expects($this->once())->method('createView')->willReturn($formView);
|
||||
|
||||
$twig = $this->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock();
|
||||
$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']);
|
||||
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
$this->assertSame('bar', $response->getContent());
|
||||
}
|
||||
|
||||
public function testRenderFormSubmittedAndInvalid()
|
||||
{
|
||||
$formView = new FormView();
|
||||
|
||||
$form = $this->getMockBuilder(FormInterface::class)->getMock();
|
||||
$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->getMockBuilder(Environment::class)->disableOriginalConstructor()->getMock();
|
||||
$twig->expects($this->once())->method('render')->with('foo', ['myForm' => $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'], null, 'myForm');
|
||||
|
||||
$this->assertSame(422, $response->getStatusCode());
|
||||
$this->assertSame('bar', $response->getContent());
|
||||
}
|
||||
|
||||
public function testStreamTwig()
|
||||
{
|
||||
$twig = $this->createMock(Environment::class);
|
||||
@ -424,91 +471,6 @@ class AbstractControllerTest extends TestCase
|
||||
$this->assertInstanceOf(StreamedResponse::class, $controller->stream('foo'));
|
||||
}
|
||||
|
||||
public function testHandleFormNotSubmitted()
|
||||
{
|
||||
$form = $this->createMock(FormInterface::class);
|
||||
$form->expects($this->once())->method('isSubmitted')->willReturn(false);
|
||||
|
||||
$controller = $this->createController();
|
||||
$response = $controller->handleForm(
|
||||
$form,
|
||||
Request::create('https://example.com'),
|
||||
function (FormInterface $form, $data, Request $request): Response {
|
||||
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
|
||||
},
|
||||
function (FormInterface $form, $data, Request $request): Response {
|
||||
return new Response('rendered');
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertTrue($response->isSuccessful());
|
||||
$this->assertSame('rendered', $response->getContent());
|
||||
}
|
||||
|
||||
public function testHandleFormInvalid()
|
||||
{
|
||||
$form = $this->createMock(FormInterface::class);
|
||||
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
|
||||
$form->expects($this->once())->method('isValid')->willReturn(false);
|
||||
|
||||
$controller = $this->createController();
|
||||
$response = $controller->handleForm(
|
||||
$form,
|
||||
Request::create('https://example.com'),
|
||||
function (FormInterface $form, $data, Request $request): Response {
|
||||
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
|
||||
},
|
||||
function (FormInterface $form, $data, Request $request): Response {
|
||||
return new Response('rendered');
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode());
|
||||
$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, $data, Request $request): Response {
|
||||
return new RedirectResponse('https://example.com/redir', Response::HTTP_SEE_OTHER);
|
||||
},
|
||||
function (FormInterface $form, $data, Request $request): 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 testHandleFormTypeError()
|
||||
{
|
||||
$form = $this->createMock(FormInterface::class);
|
||||
$form->expects($this->once())->method('isSubmitted')->willReturn(true);
|
||||
$form->expects($this->once())->method('isValid')->willReturn(false);
|
||||
|
||||
$controller = $this->createController();
|
||||
|
||||
$this->expectException(\TypeError::class);
|
||||
$this->expectExceptionMessage('The "$render" callable passed to "Symfony\Bundle\FrameworkBundle\Tests\Controller\TestAbstractController::handleForm()" must return a Response, "string" returned.');
|
||||
|
||||
$response = $controller->handleForm(
|
||||
$form,
|
||||
Request::create('https://example.com'),
|
||||
function () { return 'abc'; },
|
||||
function () { return 'abc'; }
|
||||
);
|
||||
}
|
||||
|
||||
public function testRedirectToRoute()
|
||||
{
|
||||
$router = $this->createMock(RouterInterface::class);
|
||||
|
Reference in New Issue
Block a user