September 29, 2017

Table of contents

  1. Introduction
  2. OOP in Drupal 8: what differs Drupal 8 from Drupal 7
  3. Custom module creation in Drupal 8
  4. Conclusion

OOP in Drupal 8

Introduction

I had only one year of experience in the web development in general, and basic knowledge of Drupal 7 in particular when I faced Drupal 8 for the first time. When I started learning Drupal 8 I was surprised by the fact that getting to know the new version is rather easy. That’s how this little tutorial was born: I understood the way you can start working with Drupal 8 faster and now you can learn from my experience.

What do you need to know to start developing in Drupal 8? It doesn’t matter whether you have worked with Drupal’s earlier versions or not: development in Drupal 8 differs a lot. One of the main reasons - switching from procedural programming to the object-oriented approach. It means that now the biggest part of your code is placed in classes. It can be quite disturbing for programmers, but that’s how the modern programming practices work.

OOP in Drupal 8: what differs Drupal 8 from Drupal 7

Drupal 7 operates the solutions that are not similar to the Symfony’s or Wordpress’ ones. There is a lot of own Drupal solutions like Form API or Drupal DBAL, the functionality for batching process, the hooks system, etc. Learning all of these things can be tedious and difficult. You should always keep in mind weak places and particular qualities of different Drupal components. For instance, if you want to start writing a new module for Drupal 7, you should know how Drupal 7 works, how different hooks process and many more things. And, of course, you must read a lot of documentation beforehand. Yes, it’s difficult and takes much time.

Drupal 8 is easier for development in comparison with the earlier Drupal versions. If you know the key OOP principles, design patterns (like Event Dispatcher, Factory or Dependency Injection) - you’ll be able to understand how the new system works.

What are the main differences that you should pay attention to while developing a Drupal module?

OOP usage

It’s much easier now for complete Drupal beginners to get to know this CMS if they know OOP and design patterns. For example, while bootstrapping Drupal the default container initializes and different services become available. What are those services? In a plain language, service is a class that implements a particular functionality. You can create your own class, define it as a service and make it available in the container.

Drupal developers plan to completely refuse hooks in the version 8. The pattern Event Dispatcher is used instead. This decision was based on hooks’ drawbacks, for example, hook_invoke_all is rather resource demanding one.

All in all, you don’t have to learn the hooks system now. If you know how a particular pattern (for example, (Event Dispatcher, Dependency Injection) works, you should only implement it in your module.

For example, you have to use hook_node_type_insert() to create a new content type in Drupal 7. Now you only should implement Dispatcher and bind your method 'createRequest' to the event that was defined in the method getSubscribedEvents. See the example below:

public static function getSubscribedEvents() {
    // Subscribe to kernel request with default priority of 0.
    $events[ EntityTypeEvents::CREATE][] = array('createRequest');
    return $events;
  }

Besides that, you couldn’t redefine the core’s functionality with the hooks system. The transition to OOP releases many options: the possibility to build more complex architecture, data encapsulation, methods’ redefining.

Symfony components usage

Symfony components are the universal and flexible solutions, especially in comparison with Drupal’s solutions in the earlier versions: they proved themselves as stable ones and they can solve much more problems. You know that it isn’t a good practice - to reinvent the wheel, - so don’t be afraid to use ready-made solutions. Also, Symfony follows the standards such as PSR-4, PSR-7. Symfony’s components are used in the Drupal core, so it is likely to facilitate Drupal’s transition to the standards’ usage. When it happens, it will simplify the Drupal’s learning curve.

Annotations usage

Drupal 8 uses annotations (the comments in your code that contain meta information). The main advantage of the annotations is that they improve performance due to the less memory usage. The annotations are placed in the same file as a class is. Below you can see the example of the annotations’ usage at the block’s class.

/**
 * Provides a 'Custom' Block
 *
 * @Block(
 *   id = "custom_block",
 *   admin_label = "Custom block",
 * )
 */

Drupal 8 routing

In Drupal 8 the component Routing replaced the hook hook_menu. Now you create a controller class with the method that returns data along a certain path. For the description of routes Drupal 8 uses the YAML format. All the information about routes of a module is kept in the file MODULE_NAME.routing.yml.

Each route is described separately from the others and it must have the following characteristics:

  • a name that identifies the route;
  • a path that begins with a slash;
  • a route’s processor;
  • conditions that manage an access to the route.

Of course, that’s not the complete list of the changes in Drupal 8. However the understanding of the mentioned changes is enough to write a custom module - and that’s what we’re going to do now.

Custom module creation in Drupal 8

We have already discussed the crucial aspects of Drupal 8 development, but practice is very important. Let’s create a simple module to understand how classes are used and how the changes of Drupal 8 affect a coding process. I commented the code; it will help you to understand the process.
Our module will change status messages through the admin menu. Our module will allow to set a width, a height, and a background color of a pop-up window.

Step 1

I’ll show you the module’s structure in Drupal 8 right away: it was changed a lot. Classes are kept in the src folder now, this folder is divided into subfolders, too. In our example it’s just one class of a form.
The sctructure of module in Drupal 8
The list of the necessary parameters of the info file differs from Drupal 7. Now we specify name, core and type. Type can be any of these kinds: module, theme or profile. Our module will have the configuration form where we will set parameters of the status messages, and due to that we can add the characteristic Configure - a path to this configuration form.

name: 'Status message'
description: 'Module for changing status messages'
package: custom
core: 8.x
type: module

configure: modal_window.admin_settings

Step 2

Let’s create the routing file status_message.routing.yml next. I’ll show you how I created a route for a list, a submenu for an administrative form, and a route for the form itself.

status message.admin_config (in the format MODULE_NAME.KEY) creates the submenu: we define a path to the class SystemController and its method systemAdminMenuBlockPage' with the help of the key _controller.

The admin menu will contain a list of links, and we will specify a path to that admin menu with the help of Path.

Next we create a route for the setting form with the key modal_window.admin_settings, but this time we define a path to the class of our form in the key _form. The class will be situated in the directory of the module status_message in the folder Form.

status_message.admin_config:
 path: '/admin/config/status-message'
 defaults:
   _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
   _title: 'Settings for status messages'
 requirements:
   _permission: 'access administration pages'

modal_window.admin_settings:
 path: '/admin/config/status-message/modal-window'
 defaults:
   _form: '\Drupal\status_message\Form\StatusMessageSettings'
   _title: 'Settings for modal window'
 requirements:
   _permission: 'administer site configuration'

There are few things left to do before our form will appear in the admin panel. You should create the file status_message.links.menu.yml and place the code below in this folder. The first line, likewise in the case with the route file, allocates a place in the namespace. This line is the key. In the parent we indicate the parent menu link. In the route_name we indicate the key from the routing file and that key should be relevant to the link we talked about earlier. It means that the parent of status_message.admin_config is system.admin_config: thus the submenu should be at the address admin/config. Yes, we wrote the addresses in the routing file, but without the links.menu.yml file. If you switched to the address /admin/config/status-message back then, you would see the submenu configured in the routing file - but you wouldn’t find anything in the list of links at the address admin/config.

The form of configuration of the status messages has the parent status_message.admin_config due to the fact that we built the hierarchy of links.

status_message.admin_config:
 title: 'Status message'
 parent: system.admin_config
 route_name: status_message.admin_config
 weight: -50

modal_window.admin_settings:
 title: 'Modal window'
 description: 'In this settings you can set width, height and background for modal window of status message.'
 parent: status_message.admin_config
 route_name: modal_window.admin_settings
 weight: 1

Now if you go to /admin/config/status-message, you’ll see this:
Settings for status messages
You can’t proceed to the configuration page Modal window because we didn’t create the class of the form. Let’s do it right now!

Step 3

Create the file StatusMessageSettings.php with the address status_message/src/Form. In the first part we talked about the PSR-4 standard and an autoloading of classes. If you have a look at “use”, you’ll see the list of the classes that we upload for the further usage. Define a way to our class in Namespace. According to the standard, we omit the src folder then.

The class StatusMessageSettings is inherited from the class ConfigFormBase and even expands its possibilities. What happens here: using the OOP principles, we describe our class that is based on the already existing class; at the same time, we don’t have to bother about implementation of the class ConfigFormBase.

In the method getFormId we only return the unique form’s ID, in the method getEditableConfigNames() we return the key settings form. Bear in mind: any class that expands ConfigFormBase must implement the method getEditableConfigNames and return the array of the configuration’s names of the fields.

In the method buildForm we describe the way our form will look like: what fields, width, height, and background it will have.

A form’s class in Drupal 8 must implement the method submitForm, that’s why the last thing we have to do is to add the submit method that will return the form’s values.

<?php
namespace Drupal\status_message\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
/**
 * Defines a form that configures forms module settings.
 */

class StatusMessageSettings extends ConfigFormBase {

 /**
  * {@inheritdoc}
  */

 public function getFormId() {
   return 'status_message_settings';
 }

 /**
  * {@inheritdoc}
  */

 protected function getEditableConfigNames() {
   // Return name config file.
   return [
     'status_message.settings',
   ];
 }

 /**
  * {@inheritdoc}
  */

 public function buildForm(array $form, FormStateInterface $form_state) {
   $config = $this->config('status_message.settings');
   $form['width'] = [
     '#type' => 'number',
     '#title' => $this->t('The max-width of the pop-up window in pixels.'),
     '#min' => 100,
     '#default_value' => 800,
   ];
   $form['height'] = [
     '#type' => 'number',
     '#title' => $this->t('The height of the pop-up window in pixels.'),
     '#min' => 50,
   ];

   $form['background'] = [
     '#type' => 'color',
     '#title' => $this->t('The background of the pop-up window.'),
     '#default_value' => '#efefef',
   ];

   return parent::buildForm($form, $form_state);
 }

 /**
  * {@inheritdoc}
  */

 public function submitForm(array &$form, FormStateInterface $form_state) {
   $values = $form_state->getValues();
   $width = $values['width'];
   $height = $values['height'];
   if(($width >= 100 || $width == '') && ($height >= 50 || $height == '')) {
     $this->config('status_message.settings')
       ->set('width', $values['width'])
       ->set('height', $values['height'])
       ->set('background', $values['background'])
       ->save();
   } else $form_state->setErrorByName('width or height', $this->t('Height can not be less than 50. Width can not be less than 100. Enter the correct value.'));
 }

Now when you go to admin/config/status-message/modal-window, you’ll see this form:
Settings for modal window

Step 4

We created the admin form; the thing is, the values that we can set there don’t affect the way the status messages look like. Let’s correct it.

Create the file status_message.libraries.yml (it’s the replacement of the characteristics stylesheets and scripts for the switch-on of JS and CSS files in Drupal 7) where we will specify dependencies. Drupal 8 doesn't add jQuery to each page jQuery on all pages by default: only where it’s needed. For that reason, we will define that the library of our module (file modal_window_jq.js) depends on the library that contains jQuery (core/drupal.dialog.ajax).

modal_window:
 version: 1.x
 css:
   theme:
     css/modal_window.css: {}
 js:
   js/modal_window_jq.js: {}
 dependencies:
     - core/drupal.dialog.ajax

The next step is creating the file status_message.module. Unfortunately, we are forced to use two hooks here: the first hands over values of form’s fields to a JS file so that the parameters of the status messages are changed; the second turns on the Twig file that is the replacement of the template.php file in Drupal 8.

We included one JS file in the file libraries.yml earlier; now we can turn another one that doesn’t depend on jQuery with the help of attachments.

An access to the values of form’s fields can be obtained by
$config = \Drupal::config('status_message.settings');
But we can also do it with the help of use tag for class download like we did it before.

<?php

/**
* Implements hook_page_attachments().
*/

function status_message_page_attachments(array &$attachments) {
 $config = \Drupal::config('status_message.settings');
 $attachments['#attached']['library'][] = 'status_message/modal_window';
 $attachments['#attached']['drupalSettings']['statusMessage']['modalWindow']['width'] = $config->get('width');
 $attachments['#attached']['drupalSettings']['statusMessage']['modalWindow']['height'] = $config->get('height');
 $attachments['#attached']['drupalSettings']['statusMessage']['modalWindow']['background'] = $config->get('background');
}

/**
* Implements hook_theme_registry_alter().
*/

function status_message_theme_registry_alter(&$theme_registry) {
 $theme_registry['status_messages']['theme paths'] = array(0 => drupal_get_path('module', 'status_message') . '/templates');
 $theme_registry['status_messages']['theme path'] = drupal_get_path('module', 'status_message') . '/templates';
 $theme_registry['status_messages']['path'] = drupal_get_path('module', 'status_message') . '/templates';
 // tell the theme system to use 'status_message.html.twig' as the template file. Note that you do not include 'html.twig'
 $theme_registry['status_messages']['template'] = 'status_message';
 // for check big message dpm($theme_registry)
}

Next create the file status_message.html.twig with the address status_message/templates. As it was said before, Drupal uses Twig template engine. Our Twig inherits from status_messages.html.twig template, and that’s really important. In our example we inherit from the status_messages.html.twig template with the help of the tag extends. This way we can manage all of the aspects of the parent file and change it, too.

{% extends "@classy/misc/status-messages.html.twig" %}
{% block messages %}
   {% if message_list is not empty %}
       {{ attach_library('bartik/messages') }}
       <div class="messages__wrapper layout-container" id="m_overlay">
           <div id="modal_window">
               <div id="modal_title" class="modal_title">
                   <p class="modal_title">Status messages</p>
               </div>
               {{ parent() }}
               <button class="btn-close" id="modal_close" type="button" aria-label="close">
                   &times;
               </button>
           </div>
       </div>
   {% endif %}
{% endblock messages %}

What I’ll do next is changing div’s styles with id modal_window. Earlier we defined this HTML structure in the Twig file. Try to change other attributes by doing the same actions to the width and height. Feel free to experiment! You can add drag-and-drop of the window, for example, and whatever you like, but this is another story that relates to JS and jQuery, but not to Drupal.

(function ($, Drupal, settings) {

 "use strict";

 Drupal.behaviors.status_message_modal_window = {
   attach: function (context, settings) {
    var modal = $('#modal_window');
    modal.css('background', drupalSettings.statusMessage.modalWindow.background);

})(jQuery, Drupal, drupalSettings);

I changed the background color of the module in the admin menu: now I receive the status messages in pink. I showed how to include a CSS file in the file libraries, so you can change it on the front-end side whatever way you like.
Status messages
Congratulations - we have the first results! We learned how to create forms, got to know Twig files and even JS a little bit. Of course, it’s a small step and we learned just the basic principles and saw the differences in development between Drupal’s versions. But I hope it was the useful and pleasant development adventure for you.

Conclusion

That’s it: we created our first module for Drupal 8. I think it wasn’t difficult for you. I really love Drupal 8 because it’s rather simple and has a great architecture. Yes, you must know OOP, design patterns, Twig and modern PHP trends. But in the Internet you can find information about everything that I’ve just mentioned. Instead of learning internal Drupal mechanisms, you should learn only the basics of web development that can be applied to other technologies. A large part of the PHP frameworks uses the same Symfony components, Twig and the same design patterns. This way learning Drupal 8 you’re learning how the most of PHP-frameworks work. Have fun!

Was this article helpful? Click to rate: 
Average: 4.6 (9 votes)

You may also like

Preface A lot of time has passed since the release of Drupal 8....
Introduction Sooner or later every developer faces this scary (...
A year ago Dries Buytaert told that those are the content authors and...