Dependency injection ============================== :Published at: March 10, 2020 :Author: Erlend ter Maat :Tags: Drupal, Drupal service container, Dependency injection ---- `Drupal services `_ offer a way to expose functionality to other modules. Other modules commonly use that services by means of dependency injection. Technical implementation ------------------------ Dependency injection requires a mapping where the services are defined, and a way to inject the services into objects as they are loaded. Drupal borrowed the dependency injection functionality from the symfony framework. Hence, Services are stored in yaml files. A basic example. .. code-block:: services: custom_service: class: [ class name ] another_service: class: [ another class name ] Frameworks exists, like `Masonite (Python) `_ or `Laravel (PHP) `_, where services are recognized automatically. At drupal you have to be explicit. Next two examples show the two common ways to add a service by the id of 'another_service' to the 'custom_service': By means of services ^^^^^^^^^^^^^^^^^^^^ At the services.yml file the dependency is defined a an argument. .. code-block:: # module-name.services.yml services: custom_service: class: [ class name ] arguments: - '@another_service' At the implementation of the class the dependencies are implemented / processed at the constructor. .. code-block:: php class CustomService { protected $anotherService; public function __construct(AnotherServiceInterface $anotherService) { $this->anotherService = $anotherService; } } By means of Service Containers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Providing a create method that *knows* what other services are required to build the service: .. code-block:: php class CustomServiceContainingClass implements ContainerInjectionInterface { public static function create(Container $container) { // Create an instance of this class using services from the service container. return new static($container->get('another_service'); } protected $anotherService; public function __construct(AnotherServiceInterface $anotherService) { $this->anotherService = $anotherService; } } A ContainerInjection enabled class may be defined as a service, but services don't require the ContainerInjectionInterface. You only need to implement the ContainerInjectionInterface if your service is not always loaded by the drupal services system; but only/also by the class resolver. Class resolver service ---------------------- The most common case of dependency injection is by means of services - classes that require one instance per request. Dependency injection can also be used for classes where multiple instances of the same class are required / make more sense. .. code-block:: services: custom_service: class: CustomService arguments: - '@class_resolver' .. code-block:: php class CustomService { function usingTheClassResolver() { $containingInstance = $this ->classResolver ->getInstanceFromDefinition( CustomServiceContainingClass::class ); } } Benefits ^^^^^^^^ The clean thing about using this is that when something changes in the dependencies of the CustomServiceContainingClass that is the only place where the change of code happens. Without a dependency injection a developer should change all classes that lead to the place where de dependency is used. Now you add a class resolver as dependency, and this object manages the dependencies at the place where they are needed. The combination of services and dependency injection offers a nice way to implement the `single responsibility principle `_. Dependency injection is a mechanism to inventorize the external dependencies of a class. It allows you to move code that solves another problem out of the scope of code that solves a specific business case.