{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/18193","repository_url":"https://api.github.com/repos/symfony/symfony","labels_url":"https://api.github.com/repos/symfony/symfony/issues/18193/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/18193/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/18193/events","html_url":"https://github.com/symfony/symfony/pull/18193","id":141278135,"number":18193,"title":"[FrameworkBundle] Introduce autowirable ControllerTrait","user":{"login":"dunglas","id":57224,"avatar_url":"https://avatars.githubusercontent.com/u/57224?v=3","gravatar_id":"","url":"https://api.github.com/users/dunglas","html_url":"https://github.com/dunglas","followers_url":"https://api.github.com/users/dunglas/followers","following_url":"https://api.github.com/users/dunglas/following{/other_user}","gists_url":"https://api.github.com/users/dunglas/gists{/gist_id}","starred_url":"https://api.github.com/users/dunglas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dunglas/subscriptions","organizations_url":"https://api.github.com/users/dunglas/orgs","repos_url":"https://api.github.com/users/dunglas/repos","events_url":"https://api.github.com/users/dunglas/events{/privacy}","received_events_url":"https://api.github.com/users/dunglas/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/symfony/symfony/labels/Feature","name":"Feature","color":"02e10c"},{"url":"https://api.github.com/repos/symfony/symfony/labels/FrameworkBundle","name":"FrameworkBundle","color":"DDDDDD"},{"url":"https://api.github.com/repos/symfony/symfony/labels/Status:%20Needs%20Review","name":"Status: Needs Review","color":"ededed"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":14,"created_at":"2016-03-16T13:39:15Z","updated_at":"2016-03-30T19:31:39Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/18193","html_url":"https://github.com/symfony/symfony/pull/18193","diff_url":"https://github.com/symfony/symfony/pull/18193.diff","patch_url":"https://github.com/symfony/symfony/pull/18193.patch"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Branch        | master\r\n| Bug fix?      | no\r\n| New feature?  | yes\r\n| BC breaks?    | no\r\n| Deprecations? | no\r\n| Tests pass?   | yes\r\n| Fixed tickets | #16863\r\n| License       | MIT\r\n| Doc PR        | todo\r\n\r\nThis is the missing part of the new controller system and it's fully BC with the old one.\r\n\r\nUsed 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.\r\nIt 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.\r\n\r\nMagic methods of old controllers are still available if you use this new trait in actions.\r\n\r\nFor 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:\r\n\r\n```php\r\nnamespace AppBundle\\Action\\Admin;\r\n\r\nuse AppBundle\\Form\\PostType;\r\nuse AppBundle\\Utils\\Slugger;\r\nuse Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Route;\r\nuse Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerTrait;\r\nuse Symfony\\Component\\HttpFoundation\\Request;\r\nuse Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType;\r\n\r\nclass NewAction {\r\n    use ControllerTrait;\r\n\r\n    private $slugger;\r\n\r\n    public function __construct(Slugger $slugger)\r\n    {\r\n        $this->slugger = $slugger;\r\n    }\r\n\r\n    /**\r\n     * @Route(\"/new\", name=\"admin_post_new\")\r\n     */\r\n    public function __invoke(Request $request)\r\n    {\r\n        $post = new Post();\r\n        $post->setAuthorEmail($this->getUser()->getEmail());\r\n\r\n        $form = $this->createForm(PostType::class, $post)->add('saveAndCreateNew', SubmitType::class);\r\n        $form->handleRequest($request);\r\n\r\n        if ($form->isSubmitted() && $form->isValid()) {\r\n            $post->setSlug($this->slugger->slugify($post->getTitle()));\r\n            $entityManager = $this->getDoctrine()->getManager();\r\n            $entityManager->persist($post);\r\n            $entityManager->flush();\r\n\r\n            $this->addFlash('success', 'post.created_successfully');\r\n            if ($form->get('saveAndCreateNew')->isClicked()) {\r\n                return $this->redirectToRoute('admin_post_new');\r\n            }\r\n\r\n            return $this->redirectToRoute('admin_post_index');\r\n        }\r\n\r\n        return $this->render('admin/blog/new.html.twig', array(\r\n            'post' => $post,\r\n            'form' => $form->createView(),\r\n        ));\r\n    }\r\n}\r\n```\r\n\r\nAs 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.\r\n\r\nConvenience 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.\r\n\r\nAnnotations 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.\r\n\r\nUnless 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.\r\n\r\nI'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.\r\n\r\nI'll also backport tests added to the old controller test in 2.3+."},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/203597167","html_url":"https://github.com/symfony/symfony/pull/18193#issuecomment-203597167","issue_url":"https://api.github.com/repos/symfony/symfony/issues/18193","id":203597167,"user":{"login":"derrabus","id":1506493,"avatar_url":"https://avatars.githubusercontent.com/u/1506493?v=3","gravatar_id":"","url":"https://api.github.com/users/derrabus","html_url":"https://github.com/derrabus","followers_url":"https://api.github.com/users/derrabus/followers","following_url":"https://api.github.com/users/derrabus/following{/other_user}","gists_url":"https://api.github.com/users/derrabus/gists{/gist_id}","starred_url":"https://api.github.com/users/derrabus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/derrabus/subscriptions","organizations_url":"https://api.github.com/users/derrabus/orgs","repos_url":"https://api.github.com/users/derrabus/repos","events_url":"https://api.github.com/users/derrabus/events{/privacy}","received_events_url":"https://api.github.com/users/derrabus/received_events","type":"User","site_admin":false},"created_at":"2016-03-30T19:31:38Z","updated_at":"2016-03-30T19:31:38Z","body":"@dunglas If you're considering to split your trait and since splitting the trait is something I've already done in #16863, maybe we should join forces before you're duplicating my effort? Adding setters to my traits and moving the container-aware magic to `Controller` or `ControllerTrait` shouldn't be too hard."}}