feature #18193 [FrameworkBundle] Introduce autowirable ControllerTrait (dunglas)

This PR was squashed before being merged into the 3.3-dev branch (closes #18193).

Discussion
----------

[FrameworkBundle] Introduce autowirable ControllerTrait

| Q | A |
| --- | --- |
| Branch | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | no |
| Tests pass? | yes |
| Fixed tickets | #16863 |
| License | MIT |
| Doc PR | todo |

This is the missing part of the new controller system and it's fully BC with the old one.

Used together with the existing autowiring system, #17608 and [DunglasActionBundle](https://github.com/dunglas/DunglasActionBundle) it permits to inject explicit dependencies in controllers with 0 line of config. It's a great DX improvement for Symfony.
It also has a lot of another advantages including enabling to reuse controller accros frameworks and make them easier to test. See https://dunglas.fr/2016/01/dunglasactionbundle-symfony-controllers-redesigned/ for all arguments.

Magic methods of old controllers are still available if you use this new trait in actions.

For instance, the [`BlogController::newAction`](https://github.com/symfony/symfony-demo/blob/master/src/AppBundle/Controller/Admin/BlogController.php#L70) form the `symfony-demo` can now looks like:

``` php
namespace AppBundle\Action\Admin;

use AppBundle\Form\PostType;
use AppBundle\Utils\Slugger;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

class NewAction {
    use ControllerTrait;

    private $slugger;

    public function __construct(Slugger $slugger)
    {
        $this->slugger = $slugger;
    }

    /**
     * @Route("/new", name="admin_post_new")
     */
    public function __invoke(Request $request)
    {
        $post = new Post();
        $post->setAuthorEmail($this->getUser()->getEmail());

        $form = $this->createForm(PostType::class, $post)->add('saveAndCreateNew', SubmitType::class);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $post->setSlug($this->slugger->slugify($post->getTitle()));
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($post);
            $entityManager->flush();

            $this->addFlash('success', 'post.created_successfully');
            if ($form->get('saveAndCreateNew')->isClicked()) {
                return $this->redirectToRoute('admin_post_new');
            }

            return $this->redirectToRoute('admin_post_index');
        }

        return $this->render('admin/blog/new.html.twig', array(
            'post' => $post,
            'form' => $form->createView(),
        ));
    }
}
```

As you can see, there is no need to register the `slugger` service in `services.yml` anymore and the dependency is explicitly injected. In fact the container is not injected in controllers anymore.

Convenience methods still work if the `ControllerTrait` is used (of course it's not mandatory). Here I've made the choice to use an invokable class but this is 100% optional, a class can still contain several actions if wanted.

Annotations like `@Route` still work too. The old `abstract` controller isn't deprecated. There is no valid reason to deprecate it IMO. People liking using the "old" way still can.

Unless in #16863, there is only one trait. This trait/class is basically a bunch of proxy methods to help newcomers. If you want to use only some methods, or want explicit dependencies (better), just inject the service you need in the constructor and don't use the trait.

I'll create open a PR on the standard edition soon to include ActionBundle and provide a dev version of the standard edition to be able to play with this new system.

I'll also backport tests added to the old controller test in 2.3+.

**Edit:** It now uses getter injection to benefit from lazy service loading by default.

Commits
-------

1f2521e347 [FrameworkBundle] Introduce autowirable ControllerTrait
This commit is contained in:
Fabien Potencier 2017-03-02 12:20:21 -08:00
commit 50b9126f13
4 changed files with 935 additions and 0 deletions

View File

@ -21,6 +21,12 @@ CHANGELOG
Use `Symfony\Component\Console\DependencyInjection\ConfigCachePass` instead.
* Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead
* Deprecated extending `ConstraintValidatorFactory`
* Added `Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait` (requires PHP 7). Unlike the `Symfony\Bundle\FrameworkBundle\Controller\Controller`
class, this trait does not have access to the dependency injection container. Its dependencies are explicitly and lazily
injected using getter injection.
`render()`, `renderView()` and `stream()` methods can only use Twig (using the Templating component is not supported).
The `json()` method requires the Serializer component (use `Symfony\Component\HttpFoundation\JsonResponse` directly if
you do not want to use the Serializer).
3.2.0
-----

View File

@ -0,0 +1,429 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* Common features needed in controllers.
*
* The recommended way of injecting dependencies is trough getter injection.
*
* @author Kévin Dunglas <dunglas@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in version 3.3
*/
trait ControllerTrait
{
/**
* @required
*/
protected function getRouter(): RouterInterface
{
}
/**
* @required
*/
protected function getRequestStack(): RequestStack
{
}
/**
* @required
*/
protected function getHttpKernel(): HttpKernelInterface
{
}
/**
* @required
*/
protected function getSerializer(): SerializerInterface
{
}
/**
* An instance of the Session implementation (and not the interface) is returned because getFlashBag is not part of
* the interface.
*
* @required
*/
protected function getSession(): Session
{
}
/**
* @required
*/
protected function getAuthorizationChecker(): AuthorizationCheckerInterface
{
}
/**
* @required
*/
protected function getTwig(): \Twig_Environment
{
}
/**
* @required
*/
protected function getDoctrine(): ManagerRegistry
{
}
/**
* @required
*/
protected function getFormFactory(): FormFactoryInterface
{
}
/**
* @required
*/
protected function getTokenStorage(): TokenStorageInterface
{
}
/**
* @required
*/
protected function getCsrfTokenManager(): CsrfTokenManagerInterface
{
}
/**
* Generates a URL from the given parameters.
*
* @param string $route The name of the route
* @param mixed $parameters An array of parameters
* @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface)
*
* @return string The generated URL
*
* @see UrlGeneratorInterface
*/
protected function generateUrl(string $route, array $parameters = array(), int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
return $this->getRouter()->generate($route, $parameters, $referenceType);
}
/**
* Forwards the request to another controller.
*
* @param string $controller The controller name (a string like BlogBundle:Post:index)
* @param array $path An array of path parameters
* @param array $query An array of query parameters
*
* @return Response A Response instance
*/
protected function forward(string $controller, array $path = array(), array $query = array()): Response
{
$request = $this->getRequestStack()->getCurrentRequest();
$path['_forwarded'] = $request->attributes;
$path['_controller'] = $controller;
$subRequest = $request->duplicate($query, null, $path);
return $this->getHttpKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
/**
* Returns a RedirectResponse to the given URL.
*
* @param string $url The URL to redirect to
* @param int $status The status code to use for the Response
*
* @return RedirectResponse
*/
protected function redirect(string $url, int $status = 302): RedirectResponse
{
return new RedirectResponse($url, $status);
}
/**
* Returns a RedirectResponse to the given route with the given parameters.
*
* @param string $route The name of the route
* @param array $parameters An array of parameters
* @param int $status The status code to use for the Response
*
* @return RedirectResponse
*/
protected function redirectToRoute(string $route, array $parameters = array(), int $status = 302): RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
/**
* Returns a JsonResponse that uses the serializer component.
*
* @param mixed $data The response data
* @param int $status The status code to use for the Response
* @param array $headers Array of extra headers to add
* @param array $context Context to pass to serializer
*
* @return JsonResponse
*/
protected function json($data, int $status = 200, array $headers = array(), array $context = array()): JsonResponse
{
$json = $this->getSerializer()->serialize($data, 'json', array_merge(array(
'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS,
), $context));
return new JsonResponse($json, $status, $headers, true);
}
/**
* Returns a BinaryFileResponse object with original or customized file name and disposition header.
*
* @param \SplFileInfo|string $file File object or path to file to be sent as response
* @param string|null $fileName File name to be sent to response or null (will use original file name)
* @param string $disposition Disposition of response ("attachment" is default, other type is "inline")
*
* @return BinaryFileResponse
*/
protected function file($file, string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse
{
$response = new BinaryFileResponse($file);
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName);
return $response;
}
/**
* Adds a flash message to the current session for type.
*
* @param string $type The type
* @param string $message The message
*
* @throws \LogicException
*/
protected function addFlash(string $type, string $message)
{
$this->getSession()->getFlashBag()->add($type, $message);
}
/**
* Checks if the attributes are granted against the current authentication token and optionally supplied object.
*
* @param mixed $attributes The attributes
* @param mixed $object The object
*
* @return bool
*
* @throws \LogicException
*/
protected function isGranted($attributes, $object = null): bool
{
return $this->getAuthorizationChecker()->isGranted($attributes, $object);
}
/**
* Throws an exception unless the attributes are granted against the current authentication token and optionally
* supplied object.
*
* @param mixed $attributes The attributes
* @param mixed $object The object
* @param string $message The message passed to the exception
*
* @throws AccessDeniedException
*/
protected function denyAccessUnlessGranted($attributes, $object = null, string $message = 'Access Denied.')
{
if (!$this->isGranted($attributes, $object)) {
$exception = $this->createAccessDeniedException($message);
$exception->setAttributes($attributes);
$exception->setSubject($object);
throw $exception;
}
}
/**
* Returns a rendered view.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
*
* @return string The rendered view
*/
protected function renderView(string $view, array $parameters = array()): string
{
return $this->getTwig()->render($view, $parameters);
}
/**
* Renders a view.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
* @param Response $response A response instance
*
* @return Response A Response instance
*/
protected function render(string $view, array $parameters = array(), Response $response = null): Response
{
if (null === $response) {
$response = new Response();
}
return $response->setContent($this->getTwig()->render($view, $parameters));
}
/**
* Streams a view.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
* @param StreamedResponse $response A response instance
*
* @return StreamedResponse A StreamedResponse instance
*/
protected function stream(string $view, array $parameters = array(), StreamedResponse $response = null): StreamedResponse
{
$twig = $this->getTwig();
$callback = function () use ($twig, $view, $parameters) {
$twig->display($view, $parameters);
};
if (null === $response) {
return new StreamedResponse($callback);
}
$response->setCallback($callback);
return $response;
}
/**
* Returns a NotFoundHttpException.
*
* This will result in a 404 response code. Usage example:
*
* throw $this->createNotFoundException('Page not found!');
*
* @param string $message A message
* @param \Exception|null $previous The previous exception
*
* @return NotFoundHttpException
*/
protected function createNotFoundException(string $message = 'Not Found', \Exception $previous = null): NotFoundHttpException
{
return new NotFoundHttpException($message, $previous);
}
/**
* Returns an AccessDeniedException.
*
* This will result in a 403 response code. Usage example:
*
* throw $this->createAccessDeniedException('Unable to access this page!');
*
* @param string $message A message
* @param \Exception|null $previous The previous exception
*
* @return AccessDeniedException
*/
protected function createAccessDeniedException(string $message = 'Access Denied.', \Exception $previous = null): AccessDeniedException
{
return new AccessDeniedException($message, $previous);
}
/**
* Creates and returns a Form instance from the type of the form.
*
* @param string $type The fully qualified class name of the form type
* @param mixed $data The initial data for the form
* @param array $options Options for the form
*
* @return FormInterface
*/
protected function createForm(string $type, $data = null, array $options = array()): FormInterface
{
return $this->getFormFactory()->create($type, $data, $options);
}
/**
* Creates and returns a form builder instance.
*
* @param mixed $data The initial data for the form
* @param array $options Options for the form
*
* @return FormBuilderInterface
*/
protected function createFormBuilder($data = null, array $options = array()): FormBuilderInterface
{
return $this->getFormFactory()->createBuilder(FormType::class, $data, $options);
}
/**
* Get a user from the Security Token Storage.
*
* @return mixed
*
* @throws \LogicException If SecurityBundle is not available
*
* @see TokenInterface::getUser()
*/
protected function getUser()
{
if (null === $token = $this->getTokenStorage()->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
return $user;
}
/**
* Checks the validity of a CSRF token.
*
* @param string $id The id used when generating the token
* @param string $token The actual token sent with the request that should be validated
*
* @return bool
*/
protected function isCsrfTokenValid(string $id, string $token): bool
{
return $this->getCsrfTokenManager()->isTokenValid(new CsrfToken($id, $token));
}
}

View File

@ -0,0 +1,318 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;
use Doctrine\Common\Persistence\ManagerRegistry;
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Controller\UseControllerTraitController;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\User\User;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*
* @requires PHP 7
*/
class ControllerTraitTest extends PHPUnitTestCase
{
public function testGenerateUrl()
{
$router = $this->getMockBuilder(RouterInterface::class)->getMock();
$router->expects($this->once())->method('generate')->willReturn('/foo');
$controller = new UseControllerTraitController();
$controller->setRouter($router);
$this->assertEquals('/foo', $controller->generateUrl('foo'));
}
public function testForward()
{
$request = Request::create('/');
$request->setLocale('fr');
$request->setRequestFormat('xml');
$requestStack = new RequestStack();
$requestStack->push($request);
$httpKernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock();
$httpKernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) {
return new Response($request->getRequestFormat().'--'.$request->getLocale());
}));
$controller = new UseControllerTraitController();
$controller->setRequestStack($requestStack);
$controller->setHttpKernel($httpKernel);
$response = $controller->forward('a_controller');
$this->assertEquals('xml--fr', $response->getContent());
}
public function testRedirect()
{
$controller = new UseControllerTraitController();
$response = $controller->redirect('http://example.com', 301);
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertSame('http://example.com', $response->getTargetUrl());
$this->assertSame(301, $response->getStatusCode());
}
public function testRedirectToRoute()
{
$router = $this->getMockBuilder(RouterInterface::class)->getMock();
$router->expects($this->once())->method('generate')->willReturn('/foo');
$controller = new UseControllerTraitController();
$controller->setRouter($router);
$response = $controller->redirectToRoute('foo');
$this->assertInstanceOf(RedirectResponse::class, $response);
$this->assertSame('/foo', $response->getTargetUrl());
$this->assertSame(302, $response->getStatusCode());
}
public function testGetUser()
{
$user = new User('user', 'pass');
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue(new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER'))));
$controller = new UseControllerTraitController();
$controller->setTokenStorage($tokenStorage);
$this->assertSame($controller->getUser(), $user);
}
public function testGetUserAnonymousUserConvertedToNull()
{
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue(new AnonymousToken('default', 'anon.')));
$controller = new UseControllerTraitController();
$controller->setTokenStorage($tokenStorage);
$this->assertNull($controller->getUser());
}
public function testGetUserWithEmptyTokenStorage()
{
$tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock();
$tokenStorage
->expects($this->once())
->method('getToken')
->will($this->returnValue(null));
$controller = new UseControllerTraitController();
$controller->setTokenStorage($tokenStorage);
$this->assertNull($controller->getUser());
}
public function testJsonWithSerializer()
{
$serializer = $this->getMockBuilder(SerializerInterface::class)->getMock();
$serializer
->expects($this->once())
->method('serialize')
->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS))
->will($this->returnValue('[]'));
$controller = new UseControllerTraitController();
$controller->setSerializer($serializer);
$response = $controller->json(array());
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertEquals('[]', $response->getContent());
}
public function testJsonWithSerializerContextOverride()
{
$serializer = $this->getMockBuilder(SerializerInterface::class)->getMock();
$serializer
->expects($this->once())
->method('serialize')
->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context'))
->will($this->returnValue('[]'));
$controller = new UseControllerTraitController();
$controller->setSerializer($serializer);
$response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context'));
$this->assertInstanceOf(JsonResponse::class, $response);
$this->assertEquals('[]', $response->getContent());
$response->setEncodingOptions(JSON_FORCE_OBJECT);
$this->assertEquals('{}', $response->getContent());
}
public function testAddFlash()
{
$flashBag = new FlashBag();
$session = $this->getMockBuilder(Session::class)->getMock();
$session->method('getFlashBag')->willReturn($flashBag);
$controller = new UseControllerTraitController();
$controller->setSession($session);
$controller->addFlash('foo', 'bar');
$this->assertSame(array('bar'), $flashBag->get('foo'));
}
public function testIsGranted()
{
$authorizationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true);
$controller = new UseControllerTraitController();
$controller->setAuthorizationChecker($authorizationChecker);
$this->assertTrue($controller->isGranted('foo'));
}
/**
* @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException
*/
public function testDenyAccessUnlessGranted()
{
$authorizationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock();
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false);
$controller = new UseControllerTraitController();
$controller->setAuthorizationChecker($authorizationChecker);
$controller->denyAccessUnlessGranted('foo');
}
public function testRenderView()
{
$twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock();
$twig->expects($this->once())->method('render')->willReturn('bar');
$controller = new UseControllerTraitController();
$controller->setTwig($twig);
$this->assertEquals('bar', $controller->renderView('foo'));
}
public function testRenderTwig()
{
$twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock();
$twig->expects($this->once())->method('render')->willReturn('bar');
$controller = new UseControllerTraitController();
$controller->setTwig($twig);
$this->assertEquals('bar', $controller->render('foo')->getContent());
}
public function testStreamTwig()
{
$twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock();
$controller = new UseControllerTraitController();
$controller->setTwig($twig);
$this->assertInstanceOf(StreamedResponse::class, $controller->stream('foo'));
}
public function testCreateNotFoundException()
{
$controller = new UseControllerTraitController();
$this->assertInstanceOf(NotFoundHttpException::class, $controller->createNotFoundException());
}
public function testCreateAccessDeniedException()
{
$controller = new UseControllerTraitController();
$this->assertInstanceOf(AccessDeniedException::class, $controller->createAccessDeniedException());
}
public function testCreateForm()
{
$form = $this->getMockBuilder(FormInterface::class)->getMock();
$formFactory = $this->getMockBuilder(FormFactoryInterface::class)->getMock();
$formFactory->expects($this->once())->method('create')->willReturn($form);
$controller = new UseControllerTraitController();
$controller->setFormFactory($formFactory);
$this->assertEquals($form, $controller->createForm('foo'));
}
public function testCreateFormBuilder()
{
$formBuilder = $this->getMockBuilder(FormBuilderInterface::class)->getMock();
$formFactory = $this->getMockBuilder(FormFactoryInterface::class)->getMock();
$formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder);
$controller = new UseControllerTraitController();
$controller->setFormFactory($formFactory);
$this->assertEquals($formBuilder, $controller->createFormBuilder('foo'));
}
public function testGetDoctrine()
{
$doctrine = $this->getMockBuilder(ManagerRegistry::class)->getMock();
$controller = new UseControllerTraitController();
$controller->setDoctrine($doctrine);
$this->assertSame($doctrine, $controller->getDoctrine());
}
public function testIsCsrfTokenValid()
{
$csrfTokenManager = $this->getMockBuilder(CsrfTokenManagerInterface::class)->getMock();
$csrfTokenManager->expects($this->once())->method('isTokenValid')->willReturn(true);
$controller = new UseControllerTraitController();
$controller->setCsrfTokenManager($csrfTokenManager);
$this->assertTrue($controller->isCsrfTokenValid('foo', 'bar'));
}
}

View File

@ -0,0 +1,182 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Controller;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
/**
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class UseControllerTraitController
{
use ControllerTrait {
getRouter as traitGetRouter;
getRequestStack as traitGetRequestStack;
getHttpKernel as traitGetHttpKernel;
getSerializer as traitGetSerializer;
getSession as traitGetSession;
getAuthorizationChecker as traitGetAuthorizationChecker;
getTwig as traitGetTwig;
getDoctrine as traitGetDoctrine;
getFormFactory as traitGetFormFactory;
getTokenStorage as traitGetTokenStorage;
getCsrfTokenManager as traitGetCsrfTokenManager;
generateUrl as public;
forward as public;
redirect as public;
redirectToRoute as public;
json as public;
file as public;
addFlash as public;
isGranted as public;
denyAccessUnlessGranted as public;
renderView as public;
render as public;
stream as public;
createNotFoundException as public;
createAccessDeniedException as public;
createForm as public;
createFormBuilder as public;
getUser as public;
isCsrfTokenValid as public;
}
private $router;
private $httpKernel;
private $serializer;
private $authorizationChecker;
private $session;
private $twig;
private $doctrine;
private $formFactory;
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
protected function getRouter(): RouterInterface
{
return $this->router ?? $this->traitGetRouter();
}
public function setRequestStack(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
protected function getRequestStack(): RequestStack
{
return $this->requestStack ?? $this->traitGetRequestStack();
}
public function setHttpKernel(HttpKernelInterface $httpKernel)
{
$this->httpKernel = $httpKernel;
}
protected function getHttpKernel(): HttpKernelInterface
{
return $this->httpKernel ?? $this->traitGetHttpKernel();
}
public function setSerializer(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
protected function getSerializer(): SerializerInterface
{
return $this->serializer ?? $this->traitGetSerializer();
}
public function setSession(Session $session)
{
$this->session = $session;
}
protected function getSession(): Session
{
return $this->session ?? $this->traitGetSession();
}
public function setAuthorizationChecker(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
protected function getAuthorizationChecker(): AuthorizationCheckerInterface
{
return $this->authorizationChecker ?? $this->traitGetAuthorizationChecker();
}
public function setTwig(\Twig_Environment $twig)
{
$this->twig = $twig;
}
protected function getTwig(): \Twig_Environment
{
return $this->twig ?? $this->traitGetTwig();
}
public function setDoctrine(ManagerRegistry $doctrine)
{
$this->doctrine = $doctrine;
}
public function getDoctrine(): ManagerRegistry
{
return $this->doctrine ?? $this->traitGetDoctrine();
}
public function setFormFactory(FormFactoryInterface $formFactory)
{
$this->formFactory = $formFactory;
}
protected function getFormFactory(): FormFactoryInterface
{
return $this->formFactory ?? $this->traitGetFormFactory();
}
public function setTokenStorage(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
protected function getTokenStorage(): TokenStorageInterface
{
return $this->tokenStorage ?? $this->traitGetTokenStorage();
}
public function setCsrfTokenManager(CsrfTokenManagerInterface $csrfTokenManager)
{
$this->csrfTokenManager = $csrfTokenManager;
}
protected function getCsrfTokenManager(): CsrfTokenManagerInterface
{
return $this->csrfTokenManager ?? $this->traitGetCsrfTokenManager();
}
}