How to implement Dependency Injection Design Pattern in Drupal 8?

reviewing financial reports in returning on investment analysis

The most common Services injected into an Instance of a class are usually classes that Implements these interfaces: EntityTypeManagerInterface, Connection, ConfigFactoryInterface. The concept is to Abstract the implementation, and use the Interface definition in order to write logic to connect to the Data Base, retrieve an Entity that Implements ContentEntityInterface this could be a Node or a Term, it does not really matter for our class because all methods such as toUrl(), hasField(), ->getFieldDefinition(''), ->get('')->isEmpty(), ->get('')->getValue() are available for both Classes as long as they implement such Interface.

This strategy build an architecture to organise the Classes into a logic way so that we minimise redundancy and we loaded only Classes that are necessary for each specific page load. Therefore is highly recommended to have a look around to see all the Available Core Service and try to re-use them in our Custom Services, so we write in our module only the necessary custom logics and make them re-usable as much as possible.

My advice is to plan and make a strategy, basically make a list of all the Custom and Contributed Modules and Classes that can be used in the project and Define the Interaction Between them.

Man near keyboard

A service is a class which has got a set of common functionalities which can be reused by other module. In order to define your service you need to add the following lines inside .services.yml:

    class: '\Drupal\maria_custom\MariaCustomService'
    arguments: ['@entity_type.manager', '@entity_field.manager', '@database', '@config.factory', '@logger.factory', '@date.formatter', '@messenger', '@session', '', '@current_user']

If you are interested to look at the entire definition please have a look at Develop a Custom Service in Drupal 8.

Man near keyboard

To reuse the Service Class into another class, you have to include it inside your file, in my example below you can see how I did inject my Custom Service into my Custom Block:

namespace Drupal\maria_custom\Plugin\Block;

use Drupal\maria_custom\MariaCustomService;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

 * Provides a block with 4 elements showing more services.
 * @Block(
 *   id = "maria_custom_service_block",
 *   admin_label = @Translation("More Services"),
 *   category = @Translation("Maria Custom block"),
 * )
class MoreServiceBlock extends BlockBase implements ContainerFactoryPluginInterface {

   * Custom Module to handle all the Storage managers.
   * @var MariaCustomService
  protected $customService;

Things important to notice here are:

  • Include the MariaCustomService as you usually would to use any other Class
  • Add a protected field in your class to keep in $this->customService the instance of the Service
  • Then this variable is set inside the __construct then the Object is created:
  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match, MariaCustomService $customService) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->route_match = $route_match;
    $this->customService = $customService;

Then the Custom service is passed through the Factory Method create that has got as first argument ContainerInterface $container, if you are interested read more details on how to Create a custom block in Drupal 8 with Node and taxonomy.

Man near keyboard

To make the code as generic as possible, please try to keep your code as generic as possible by always using Interfaces and never use specific Class Names such as Term and Node:

  • Inside a Service never use Node::load(), please use the Entity Storage Manager instead.
  • Inside your methods never pass the specific class: Node or Term, always pass: ContentEntityInterface
  • To check if the Storage Manager return an entity check for instance of ContentEntityInterface

For example this code is wrong:

$node = Node::load($nid);
    if ($node instanceof Node) {

and this code is correct:

$node = $this->nodeStorage->load($nid);
    if ($node instanceof ContentEntityInterface) {

The idea Behind Dependency Injection is that the Factory Create() Method always Injects into your class Instance a class that Implement an Interface so if in future the Implementation of the Core Service change, you do not need to change your code because you are using the Interface Definition. For example if in future the Node Storage Manger load may return something which is not a Node class but something else that also implements ContentEntityInterface then you need to change everywhere your code.

Keep it simple and generic, look how elegant is the signature of this function:

  public function getImageData(ContentEntityInterface $contentEntity, $field_name)

It works fantastically well for any Content Entity that has got a field Image, therefore I did not need to create 2 functions one for Node and one for Term, the same code works for both Entities.

1. Integrated

digital marketing start up development marketing data analysis

Plan your content types, permissions, roles, views and taxonomies. Set up different contexts, view modes, panel variants and view.

2. Strategy

creative web designer developing template layout

Do you need a very simple marketing brochure website or a very big robust intranet system? Plan which modules you need to install and keep it simple!

3. Install

Man near keyboard

Drupal 8 is an open source Content Management System (CMS), read our Guide on how to install it on your local environment using Docker4Drupal.

4. Drupal

develop coding web design coding web template

Create your own Theme from hundreds of existing themes and then customise it. This is a fantastic feature in Drupal to implement any kind..