Drupal service system

Published at

February 24, 2020

Author

Erlend ter Maat

Tags

Drupal, Drupal service container


Drupal did not give developers a good reason to work OO when developing custom modules or themes… Until Drupal 8!

A feature that I’ve been exploring recently is the services system.

What do services look like?

Service initialization

At a minimum a service is made known to the system like this:

# custom_module.services.yml
services:
  custom_service:
    class: Drupal\custom_module\Services\CustomService

Service body

A simple example of a service that adds the number two to an unknown parameter value.

<?php

namespace Drupal\custom_module\Services;

class CustomService {

  public function addTwo($param) {
    return $param + 2;
  }

}

How can services be used?

Next section shows the cases that I used in my projects until this day. I won’t deal with all possible use cases. I think these are the most common ones.

As a dependency at another service

# another_custom_module.services.yml
services:
  another_service:
    class: [ a qualified class name ]
    arguments:
      - '@custom_service'

As a dependency at another class

An instance of the class that is defined at the ‘custom_service’ service is injected in the constructor of the CustomForm class.

<?php

namespace Drupal\custom_module\Forms;

use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormBase;
use Drupal\custom_module\CustomServiceInterface;

class CustomForm extends FormBase implements ContainerInjectionInterface {

  protected $serviceInstance;

  public function __construct(CustomServiceInterface $serviceInstance) {
    $this->serviceInstance = $serviceInstance;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('custom_service')
    );
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    // Build the form.
    // ...

    // Use the service.
    $maybeTen = $this->serviceInstance->addTwo(8);
  }
}

As a dependency at a plugin

<?php

namespace Drupal\custom_module\Plugin\Blocks;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\custom_module\CustomServiceInterface;

/**
 * @Block(
 *   id = "custom_block",
 *   category = @Translation("Custom")
 * )
 */
class CustomBlock extends BlockBase implements ContainerFactoryPluginInterface {

  protected $serviceInstance;

  public function __construct(array $configuration, $plugin_id, $plugin_definition,
                              CustomServiceInterface $serviceInstance) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->serviceInstance = $serviceInstance;
  }

  public static function create(ContainerInterface $container,
                                array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration, $plugin_id, $plugin_definition,
      $container->get('custom_service')
    );
  }

  protected function maybeTen() {
    // Use the service.
    return $this->serviceInstance->addTwo(8);
  }

}

Anywhere else

Use the service at any other place where the dependency system is not available for some reason.

<?php

/**
 * @file custom_module.module
 */

function custom_module_ANY_HOOK() {
   /** @var Drupal\custom_module\CustomServiceInterface $serviceInstance */
   $serviceInstance = Drupal::service('custom_service');

   // Use the service.
   $maybeTen = $this->serviceInstance->addTwo(8);
}

So…

Services offer a great way to expose the functionality of your custom module in a structured way to other modules. You can regard it as interfaces to the functionality your module has to offer. The services you create can be used by your own module or by other modules.

Applications

Services can be used for:

  • REST API’s.

  • Interfaces to your custom entities.

  • Interface to any kind of processing only your module knows about.

  • Some Drupal hooks moved to the services system.

  • Etc.

Next time…

With the event_subscriber tag Drupal services offer an object oriented mechanism for hooks:

# custom_module.services.yml
services:
  custom_module.route_subscriber
    class: Drupal\custom_module\Routing\CustomRouteSubscriber
    tags:
      - { name: 'event_subscriber' }

So next blog posts I will dive deeper into the services based new hook system.