-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DependencyInjection] Autowiring: add setter injection support #17608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
*/ | ||
private function autowireConstructor($id, Definition $definition, \ReflectionClass $reflectionClass) | ||
{ | ||
if (!($constructor = $reflectionClass->getConstructor())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
()
around the var def should be removed.
This feature is indeed a must have to better support the uses cases described in the linked issue. |
What if someone wants to use autowiring but only wants to use constructor autowire and not setter autowire? Or not for all methods (in that case setter autowiring should not be used). |
@sstok If a Supporting only constructor autowiring or both setter and constructor using a new flag in |
@fabpot support for |
@dunglas I love it! I just tried the code, here are the issues / concerns I found:
Thanks! |
@dunglas any feedback on @weaverryan comments? |
They are all relevants, I need to find some time to work on this. |
For the BC break, as @sstok has the same concern, I'll introduce a new |
@dunglas That will fix BC, but I'm concerned about usability - e.g.: services:
spring_weather_manager:
class: ...
autowire: true
autowire_setter: true If it weren't for the BC problem, my opinion would be: always do setter injection - after all, if it causes you any issues, don't use it. For that reason, I'd prefer a global flag to turn this on/off that goes away in 4.0. |
On the other hand, the BC break is very limited: it will only impact people using autowiring (just a few), having classes with setters for services (unlikely, because it wasn't supported in the first version of the autowiring system), and having a setter that broke the class when triggered (very very unlikely). Maybe can we say that this "BC break" is acceptable, as it's more an edge case than a BC break. |
Honestly, I'm inclined to agree. This is a "magic" feature you're opting into, it's only 1 version it's been available, it won't affect many people, and if it causes an issue, it'll happen at build time (very easy to debug). |
👍 for the small BC break instead of yet another configuration setting. |
I agree with you about accepting this small BC break (but we need to document it clearly in changelog files) |
Ok, so BC break is ok. This leaves items 2-4 #17608 (comment), when you have some time :) |
@dunglas Any idea when you will be able to finish this one? |
Before the end of this week. |
5cab494
to
f690258
Compare
All issues fixed. Can you review @weaverryan? |
Hi Dunglas! I need a few more days to get back a full report - I will make a very clear explanation of the functionality so that everyone is clear how it works (a few cases are almost subjective, so I want it to be clear to everyone). The only issue I've found is that if we cannot autowire because the type-hint is for a non-existent class, it simply doesn't call the method. That's different than the constructor where it throws an exception. Also, I think we (probably me, as I've had some recent PR's merged) may have introduced a bug into the master branch. I'm too short on time to look further right this moment, but I'm getting weird results if I type-hint something like I think we're getting quite close to mastering the exact behavior we want. We need to get it right for 3.1, because whatever we end up with will probably stay with us due to BC. |
* | ||
* @throws RuntimeException | ||
*/ | ||
private function autowireMethod($id, Definition $definition, \ReflectionMethod $reflectionMethod, $constructor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about renaming $constructor
to $isConstructor
to be clearer?
I think this PR is ready. The question is: do we want it? It's opinionated (but so is autowiring in general, and it's opt-in). Some of the new autowiring features/uses (like those in DunglasActionBundle) are quite radical, and I think they need to be tested before considering moving further forward (in the core) in that direction. However, if this were merged, it allows more of these ideas to be tested out in the wild - like using traits e.g. I'm 👍, but I don't feel as confidently about this as I have other things in the past. @symfony/deciders |
👍 LGTM |
056612c
to
5470db2
Compare
Rebased. Can be merged. |
@symfony/deciders what do we do of this PR? Should I close it or merge it? |
Re-read everything here. @weaverryan and @dunglas did a great job at defining exactly what we should do and what is out of scope. I agree with everything @weaverryan said and as this is what @dunglas implemented, I'm 👍 (I would not advocate its usage for everything but as this is an opt-in feature anyway, this looks good to me). |
@dunglas Looks like it needs a rebase |
5511bc4
to
8c34d6d
Compare
Rebased. |
@dunglas I didn't notice but history is really ugly but as there are 2 contribs, gh won't be able to squash commits. Can you do that for me? |
Should I merge all commits in 1 or should I keep the commit of @weaverryan? |
I think you can merge everything into 1 commit as @weaverryan contrib is not huge. |
786a915
to
a0d7cbe
Compare
Done. |
Thank you @dunglas. |
@dunglas Can you take care of the docs? |
… support (dunglas) This PR was merged into the 3.2-dev branch. Discussion ---------- [DependencyInjection] Autowiring: add setter injection support | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | todo Add support for setter injection in the Dependency Injection Component. Setter injection should be avoided when possible. However, there is some legitimate use cases for it. This PR follows a proposal of @weaverryan to ease using [DunglasActionBundle](https://github.com/dunglas/DunglasActionBundle) with #16863. The basic idea is to include a setter for the required service in the trait and let the DependencyInjection Component autowire the dependency using the setter. This way, a newcomer can use the trait without having to create or modify the constructor of the action. /cc @derrabus Commits ------- a0d7cbe [DependencyInjection] Autowiring: add setter injection support
Yes, I've a lot of docs to write... |
… add setter injection support (dunglas)" (nicolas-grekas) This PR was merged into the 3.2-dev branch. Discussion ---------- Revert "feature #17608 [DependencyInjection] Autowiring: add setter injection support (dunglas)" | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This reverts commit 7eab6b9, reversing changes made to 35f201f. As discussed in #20167 Commits ------- bf91eda Revert "feature #17608 [DependencyInjection] Autowiring: add setter injection support (dunglas)"
…t (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 ------- 1f2521e [FrameworkBundle] Introduce autowirable ControllerTrait
Add support for setter injection in the Dependency Injection Component.
Setter injection should be avoided when possible. However, there is some legitimate use cases for it. This PR follows a proposal of @weaverryan to ease using DunglasActionBundle with #16863. The basic idea is to include a setter for the required service in the trait and let the DependencyInjection Component autowire the dependency using the setter.
This way, a newcomer can use the trait without having to create or modify the constructor of the action.
/cc @derrabus