Site icon NexGismo

Drupal 10: Services and Dependency Injection

Drupal Services and Dependency Injection

Drupal 10 emerges as a guiding light of flexibility and scalability in the ever-changing world of web development. One of its many powerful features is that services and dependency injection (DI) make it possible to create modular, maintainable, and testable code. This article utilizes our extensive knowledge gained from working on different Drupal projects for client companies to discuss the essence of services and dependency injection in Drupal 10.

Understanding Drupal Services

What is a Service?

A service represents a PHP object that performs certain tasks. They are often singleton objects i.e. one instance exists all through the lifecycle of an application. They help you put reusable logic into one place for your application, so that you can use it across various parts of your application which encourages DRY principles or Do not Repeat Yourself.

Core Services in Drupal

Drupal 10 comes with a plethora of built-in services such as:

These core services can be extended and customized as per the application needs.

What is Dependency Injection?

Dependency Injection (DI) employs Inversion of Control (IoC), having classes accept dependencies from elsewhere instead of manufacturing them itself As such, it is used as a design pattern. Testability and flexibility are achieved by this detachment between the class using them and their creation.

Types of Dependency Injection

Real-world example

For instance, let us consider a situation where we implemented a custom email notification service for a client who required some advanced email functionalities apart from Drupal’s core capabilities.

Folder Structure


The folder structure for the custom_email module is as follows:

modules/custom/custom_email/  

├── custom_email.info.yml  
├── custom_email.routing.yml  
├── custom_email.services.yml  
└── src/  
    ├── Form/  
    │   └── CustomEmailForm.php  
    └── Service/  
        └── CustomEmailService.php

Creating a Custom Module in Drupal 10

Before diving into the implementation of services and DI, let’s walk through the steps to create a custom module in Drupal 10. This will set the foundation for understanding the structure required for our custom implementations.

Define the Module’s Info File

Make an info.yml file inside your module directory. This file contains metadata about your module. Save this file as custom_email.info.yml in the modules/custom/custom_email directory.

name: 'Custom Email'  
type: module  
description: 'A custom module for sending emails.'  
core_version_requirement: ^10  
package: Custom

Create a Services Configuration File

Create a services.yml file which defines your own services.

services:  
  custom_email.email_service:  
    class: Drupal\custom_email\Service\CustomEmailService  
    arguments: ['@plugin.manager.mail']  

Define the Service

We designed a custom email notification service. Within our custom module, this service was conceived of as a class in the src/Service directory. Save this file as CustomEmailService.php in the modules/custom/custom_email/src/Service directory.

<?php  
  
namespace Drupal\custom_email\Service;  
  
use Drupal\Core\Mail\MailManagerInterface;  
use Drupal\Core\StringTranslation\StringTranslationTrait;  
  
class CustomEmailService {  
  use StringTranslationTrait;  
  
  protected $mailManager;  
  
  public function __construct(MailManagerInterface $mail_manager) {  
    $this->mailManager = $mail_manager;  
  }  
  
  public function sendEmail($to, $subject, $body) {  
    $module = 'custom_email';  
    $key = 'custom_email_key';  
    $params['subject'] = $subject;  
    $params['body'] = $body;  
    $langcode = \Drupal::currentUser()->getPreferredLangcode();  
    $send = true;  
  
    $result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);  
  
    if ($result['result'] !== true) {  
      \Drupal::logger('custom_email')->error($this->t('There was a problem sending your email to @email.', ['@email' => $to]));  
    }  
  
    return $result;  
  }  
}  

Create the Form

Then we made a form that would utilize the custom email service to enable email notifications upon certain user actions. Save this file as CustomEmailForm.php in the modules/custom/custom_email/src/Form directory.

<?php  
  
namespace Drupal\custom_email\Form;  
  
use Drupal\Core\Form\FormBase;  
use Drupal\Core\Form\FormStateInterface;  
use Drupal\custom_email\Service\CustomEmailService;  
use Symfony\Component\DependencyInjection\ContainerInterface;  
  
class CustomEmailForm extends FormBase {  
  protected $emailService;  
  
  public function __construct(CustomEmailService $email_service) {  
    $this->emailService = $email_service;  
  }  
  
  public static function create(ContainerInterface $container) {  
    return new static(  
      $container->get('custom_email.email_service')  
    );  
  }  
  
  public function getFormId() {  
    return 'custom_email_form';  
  }  
  
  public function buildForm(array $form, FormStateInterface $form_state) {  
    $form['email'] = [  
      '#type' => 'textfield',  
      '#title' => $this->t('Email Address'),  
      '#required' => TRUE,  
    ];  
    $form['submit'] = [  
      '#type' => 'submit',  
      '#value' => $this->t('Send Email'),  
    ];  
    return $form;  
  }  
  
  public function submitForm(array &$form, FormStateInterface $form_state) {  
    $email = $form_state->getValue('email');  
    $subject = $this->t('Test Email');  
    $body = $this->t('This is a test email.');  
    $this->emailService->sendEmail($email, $subject, $body);  
    $this->messenger()->addMessage($this->t('Email sent to @email', ['@email' => $email]));  
  }  
}  

Create Route to access the form

For us to use the form, however, there is need to create a menu item or route that will display it. Here’s how: Create a file named custom_email.routing.yml in the modules/custom/custom_email directory .

custom_email.form:  
  path: '/custom-email-form'  
  defaults:  
    _title: 'Send Custom Email'  
    _form: '\Drupal\custom_email\Form\CustomEmailForm'  
  requirements:  
    _permission: 'access content'  

Benefits of Using Services and DI

Implementing services and DI offers numerous advantages, as experienced in our client project

  1. Improved Code Reusability: By encapsulating functionality into services, we ensure that code can be reused across different parts of the application.
  2. Enhanced Maintainability: Decoupling dependencies makes it easier to maintain and extend the codebase.
  3. Better Testability: With DI, services can be easily mocked during unit testing, ensuring that tests are isolated and reliable.
  4. Scalability: Services can be scaled independently, allowing for more flexible and scalable application architecture.

Conclusion

Mastering what services and dependency injection are all about in Drupal 10 is extremely necessary for developing highly efficient, maintainable and scalable applications. By employing these ideas outlined through our well-detailed real-life example you will be able to create strong solutions which meet your client’s requirements. No matter if you are an experienced developer or beginner, using these patterns will greatly boost your Drupal projects.

References

  1. Drupal 10 Official Documentation
  2. Creating Custom Modules
  3. Dependency Injection in Drupal
Exit mobile version