How to refactor to new Dependency Injection features in Symfony 3.3
This May will be released Symfony 3.3 with many DependencyInjection improvements.
Each of them is quite nice, but combined together - they are huge jump compare to what we have now.
Today I will show you what code can you drop and how to migrate it.
What is New?
Symfony 3.3+ brings new that will completely change they we register services:
autoconfigure
_defaults
andinstanceof
- named services → class services
- PSR-4-based service autodiscovery
You can click read post/PR in detail, but you take this shortcut to learn them...
"A full code example is worth ten thousand words of explanation."
Refactor Service Config in 5 Steps
This is service config in Application in Symfony 3.2 or lower.
We apply all features we can and I always add a small # comment
to the code with explanation.
# app/config/services.yml
services:
some_service:
class: App\SomeService
autowire: true
some_controller:
class: App\Controller\SomeController
autowire: true
first_repository:
class: App\Repository\FirstRepository
autowire: true
calls:
- ["setEntityManager", ["@entity_manager"]]
second_repository:
class: App\Repository\SecondRepository
autowire: true
calls:
- ["setEntityManager", ["@entity_manager"]]
first_command:
class: App\Command\FirstCommand
autowire: true
tags:
- { name: console.command }
second_command:
class: App\Command\SecondCommand
autowire: true
tags:
- { name: console.command }
1. Let's add _defaults
# app/config/services.yml
services:
_defaults:
autowire: true # all services in this config are now autowired
some_service:
class: App\SomeService
some_controller:
class: App\Controller\SomeController
first_repository:
class: App\Repository\FirstRepository
calls:
- ["setEntityManager", ["@entity_manager"]]
second_repository:
class: App\Repository\SecondRepository
calls:
- ["setEntityManager", ["@entity_manager"]]
first_command:
class: App\Command\FirstCommand
tags:
- { name: console.command }
second_command:
class: App\Command\SecondCommand
tags:
- { name: console.command }
2. Use autoconfigure
# app/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true # all Symfony native tags are now added automatically
some_service:
class: App\SomeService
some_controller:
class: App\Controller\SomeController
first_repository:
class: App\Repository\FirstRepository
calls:
- ["setEntityManager", ["@entity_manager"]]
second_repository:
class: App\Repository\SecondRepository
calls:
- ["setEntityManager", ["@entity_manager"]]
first_command:
class: App\Command\FirstCommand
second_command:
class: App\Command\SecondCommand
3. Use Class-Named Services
# app/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
App\SomeService: ~ # no more thinking about creative and unique service name
App\Controller\SomeController: ~
App\Repository\FirstRepository:
calls:
- ["setEntityManager", ["@entity_manager"]]
App\Repository\SecondRepository:
calls:
- ["setEntityManager", ["@entity_manager"]]
App\Command\FirstCommand: ~
App\Command\SecondCommand: ~
4. Use PSR-4 based service autodiscovery and registration
# app/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
App\: # no more manual registration of similar groups of services
resource: '../'
App\Repository\FirstRepository:
calls:
- ["setEntityManager", ["@entity_manager"]]
App\Repository\SecondRepository:
calls:
- ["setEntityManager", ["@entity_manager"]]
5. Use _instanceof
...
# app/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../'
_instanceof: # clean and explicit dependency injection to abstract services
App\Repository\AbstractRepository:
calls:
- ["setEntityManager", ["@entity_manager"]]
5. ...or Setter Injection
You can even remove the _instanceof
with setter injection. First, modify the abstract repository to add the @required
annotation to setEntityManager
:
<?php
namespace App\Repository;
use Doctrine\ORM\EntityManagerInterface;
abstract class AbstractRepository
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @required
*/
public function setEntityManager(EntityManagerInterface $entityManager): void
{
$this->entityManager = $entityManager;
}
// ...
}
Now, remove the _instanceof
in your services.yml
:
# app/config/services.yml
services:
_defaults:
autowire: true
autoconfigure: true
App\:
resource: '../'
You're awesome! Now you're using all the shiny new Symfony 3.3 Dependency Injection features.
What is your favorite change?
Have you find this post useful? Do you want more?
Follow me on Twitter, RSS or support me on GitHub Sponsors.