Symfony 4 Service Dependency Injection - Constructor vs Method Symfony 4 Service Dependency Injection - Constructor vs Method symfony symfony

Symfony 4 Service Dependency Injection - Constructor vs Method


Reasons to use DI in a controller's route methods:

  • Less susceptible to changes of the parent::__construct method. Using DI on the constructor means you have to adapt your code whenever this changes. Also notice that some Symfony bundles may assume that controllers have a particular signature and it might make things more complicated for you if it does not.
  • If at least one of the routes does not use the service, by using these fine-grained DIs we avoid instantiating a service when this is not necessary (which can be costly if it has its own DIs that weren't already used somewhere else). This can be mostly offset by using lazy services though.

Reasons to use DI in the contructor:

  • In services other than controllers (and in methods other than route methods of controllers, if any), you can't use autowiring. If you want to inject your dependencies using a method's argument, you'll have to manually pass that dependency with each call. This means in turn that whichever service calls that method should itself have a DI on the required service. The problem is therefore shifted, but it can't be shifted infinitely that way, and at some point you're going to want to use some autowiring on a parent.

Alternative to using DI in the constructor:

You can also use setter injection and configure your service this way. This is functionally pretty similar to using DI in the constructor, but it bypasses the major drawback of generating a different signature from the parent and being more work to maintain if the parent constructor changes.

You can also make this easier to use for services you often inject. Make the services that need this DI implement an interface and configure them with _instanceof. Symfony does that with its ContainerAwareInterface and even has a facilitator in the form of ContainerAwareTrait to declare the setter and the property. In your case, if several services require the MySqlGroupDAO service, you could define a MySqlGroupDAOAwareTrait and MySqlGroupDAOAwareInterface, add a MySqlGroupDAOAwareInterface entry in your services.yaml's _instanceof section, and use the trait and implement the interface in services that need the DI.


Edit Nov. 2021:

This answer is still being read by new people, so I thought I'd complete it with something that was added with Symfony 5.2 (and it requires PHP 8): using PHP attributes for dependency injection.

This allows Setter Injection without touching services.yaml, and it allows public property injection. Here's the doc's example for both of these:

use Symfony\Contracts\Service\Attribute\Required;class SomeService{    // Example of public property injection    #[Required]    public Bar $bar;    // Example of setter injection without editing services.yaml    #[Required]    public function setFoo(Foo $foo): void    {        // ...    }}

Notice that the property and the setter method used above need to be public (which might or might not be okay with you).

Read more at https://symfony.com/blog/new-in-symfony-5-2-php-8-attributes.