Services

Objectives
  • Learn what services are and why they make your work easier.
  • Gain a basic understanding of the service container.
  • Learn the two most common ways of accessing services.

Introduction

As of Drupal 8, Drupal was extensively rewritten to use more modern PHP coding practices, including the use of services throughout its code base.

Services, and the related concepts of dependency injection and service containers can quickly become quite complex.

This is an introductory course, so we're going to limit ourselves to explaining how to use some of the most common services and where to find more information on which services are available.

In a follow-up course we will cover more advanced service patterns such as how, when, and why to create your own services, or how to make additional services available to your classes.

What are services

Services are PHP objects that make useful functionality available to you, the developer.

For example, the current_user service returns user account information for the currently logged-in user, while the logger service lets you write notices, warnings, and error messages to Drupal's logging system.

Without such a common service layer, each developer who wants to log errors, make http requests, or send email would have to reinvent the wheel.

Many of Drupal's core services are built to be so generic they can be used by Drupal core itself, as well as contributed modules.

Some of the most often used Drupal services include:

  • Logging errors
  • Making http requests
  • Sending email
  • Making text strings translatable
  • Generating links
  • Making direct database queries

How to use services

You've already used at least one service - the translation service:

/my_route_demo/src/Controller/HelloController.php:
Code
namespace Drupal\my_route_demo\Controller;

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {

  public function hello() {
    return [
      '#markup' => $this->t('Hello. It is a fine day indeed!'),
    ];
  }

}

Using the t() method makes text translatable via Drupal's translation mechanism.

If you only care about one language, you could also simplify that line of code like this:

'#markup' => 'Hello. It is a fine day indeed!',

However it's considered a bad practice not to pass your output through the translation mechanism.

Many classes you extend from (in this case ControllerBase) come with a number of services you can use. They're all accessible inside your class via the $this keyword.

Here's another example: figuring who the current user is (so you can show a personalised welcome message, for instance)

// Step 1: retrieve current user's id.
$user_id = \Drupal::currentUser()->id();
// Step 2: figure out the user's name.
// ...

In this example we're not inside an object (like we were in the previous example) so we need to access the service we want from elsewhere.

Luckily, the service container comes to the rescue. It contains all the available services (the ones Drupal core and any contributed module make available), and is available through the globally accessible \Drupal object.

Let's compare both service calls:

Example 1:

$this->t('Hello. It is a fine day indeed!')

Example 2:

$user_id = \Drupal::currentUser()->id();

These examples illustrate the two most common ways of accessing services:

  • Through your object by using the $this keyword.
  • Through the service container, which you can access via the \Drupal class.

Should I access services through my object or through the service container?

If and when you can, it's considered best practice to access services through your object rather than via the service container.

For example, if the class you're extending contains the currentUser service, you can access it through your object:

$user_id = $this->currentUser()->id();

If it doesn't, or you don't want to access it that way for some reason, you can always use the service container:

$user_id = \Drupal::currentUser()->id();

The end result is the same, but there are subtle consequences that will start to matter later on, if and when you start writing automated tests.

For now, as a beginning Drupal coder, feel free to disregard this best practice and access services any way you're comfortable with - directly through your object, or via the global service container. On this level it really doesn't make much of a difference.

Which services are available?

That's a perfectly acceptable question at this point. We'll answer this question a bit later in this chapter, after you've had some practice using various services.

Summary

  • Drupal makes a number of useful utility services available to developers.
  • You can access these services through your own objects if they extend a class that contains the services you need.
  • All services are also present in the service container, which you can access at any time and for any reason via the global \Drupal class.