This project is not covered by Drupal’s security advisory policy.

Deprecation Notice

⚠️ This module is deprecated, and usage of IDE Plugins/extensions is preferred.

For example, for IntelliJ IDEA & PhpStorm, use the following Symfony Plugin, which supports proper services auto-complete at the IDE level.

Introduction

Welcome to Service Injector, a module dedicated to making your life easier if you do a lot of dependency injection in your Drupal projects.

Purpose

This module aims at allowing faster and standardized Dependency Injection in your custom modules by:

Structure

The module is separated into 2 main directories:

  • src/Constant, which contains CoreServices constants
  • src/Service, which contains all service-related traits (1 per service)

How To Use

When adding or adjusting the public static function create() method in your class, use the constants available in CoreServices to add your required services, e.g.:

public static function create(ContainerInterface $container) {
  return new static(
    $container->get(CoreServices::CONFIG_FACTORY),
  );
}

The desired service can be set inside your own service class constructor via the utility setter method of this trait, e.g. setConfigFactoryService():

/**
 * Your service class constructor.
 */
public function __construct(ConfigFactoryInterface $configFactoryService) {
  $this->setConfigFactoryService($configFactoryService);
}

The injected service should then be accessed via the public getter accessor that matches its name suffixed with Service, e.g. configFactoryService():

/**
 * Your service method for custom settings retrieval.
 */
public function getCustomConfig() : void {
  $settings = $this->configFactoryService()->get('custom.config')->get('custom.settings');
}

Examples

Let's say you have a custom module mymodule with a custom service pizza, which requires the config.factory and state services:

services:
  mymodule.pizza:
    class: Drupal\mymodule\Service\Pizza
    arguments:
      - '@config.factory'
      - '@state'

Normally, you would end up with something along the lines of the following code for your Drupal\mymodule\Service\Pizza class:


namespace Drupal\mymodule\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\State\StateInterface;

/**
 * Example class for the Pizza custom service, without using Service Injector.
 */
class Pizza {

  /**
   * The Drupal Config Factory service.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  private ConfigFactoryInterface $configFactoryService;

  /**
   * The Drupal State API service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  private StateInterface $stateService;

  /**
   * Pizza constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactoryService
   *   The Drupal Config Factory service.
   * @param \Drupal\Core\State\StateInterface $stateService
   *   The Drupal State API service.
   */
  public function __construct(
    ConfigFactoryInterface $configFactoryService,
    StateInterface $stateService
  ) {
    $this->configFactoryService = $configFactoryService;
    $this->stateService = $stateService;
  }

  /**
   * Bake the Pizza.
   */
  public function bake() : void {
    $config = $this->configFactoryService->get('mymodule.pizza');
    $duration = $config->get('bake.duration');
    $temperature = $config->get('bake.temperature');
    $this->stateService->set('mymodule.pizza.state', 'baking');
  }

}

With Service Injector, you can achieve the same result via the use of its traits:


namespace Drupal\mymodule\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\service_injector\Service\ConfigFactoryServiceTrait;
use Drupal\service_injector\Service\StateServiceTrait;

/**
 * Example class for the Pizza custom service, using Service Injector traits.
 */
class Pizza {
  use ConfigFactoryServiceTrait;
  use StateServiceTrait;

  /**
   * Pizza constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactoryService
   *   The Drupal Config Factory service.
   * @param \Drupal\Core\State\StateInterface $stateService
   *   The Drupal State API service.
   */
  public function __construct(
    ConfigFactoryInterface $configFactoryService,
    StateInterface $stateService
  ) {
    $this->setConfigFactoryService($configFactoryService);
    $this->setStateService($stateService);
  }

  /**
   * Bake the Pizza.
   */
  public function bake() : void {
    $config = $this->configFactoryService()->get('mymodule.pizza');
    $duration = $config->get('bake.duration');
    $temperature = $config->get('bake.temperature');
    $this->stateService()->set('mymodule.pizza.state', 'baking');
  }

}

Injecting services into your own services is now much easier, especially if many of your services require multiple other services.

If you are in a situation where your class is implementing a dependency injection interface such as ContainerInjectionInterface, thus requiring the implementation of the public static function create() method, then you can use the constants available in CoreServices:

public static function create(ContainerInterface $container) {
  return new static(
    $container->get(CoreServices::CONFIG_FACTORY),
    $container->get(CoreServices::STATE),
  );
}

If your IDE supports it, displaying the constant's docblock comment will include an @see statement indicating the class returned by $container->get():

// @see \Drupal\Core\Config\ConfigFactory
CoreServices::CONFIG_FACTORY

// @see \Drupal\Core\State\State
CoreServices::STATE

This is useful when adding new parameters to the class constructor once the public static function create() method has been adjusted. You can then find the appropriate class to properly type your constructor parameters.

Requirements

PHP minimal version required: 7.4

Image Credit

Photo by Denys Nevozhai on Unsplash

Project information

Releases