Creating modules in Drupal 8

In this article, we will look at the process of creating a module under Drupal 8.Namely: we will create two pages, one of which will add data to its "own" plate, and the second - will display its content in the desired form for
us. So let's start.

Setting up the environment

To start, you need to configure the environment for ease of writing code. You can use phpstorm to do this.

PhpStorm- is an integrated development environment in PHP with an intelligent editor that deeply understands the code, supports PHP for modern and classic projects, provides the industry's best code completion, prevents errors on the fly and supports the blending of languages.

Here you can find a link to install phpstorm:

Creating of a directory for the module

After setting up the environment, we need to create a directory for the module in the path / modules / custom / mypage (in the Drupal 7 modules are / sites / all / modules).

Creating of an .info file

Next we need to give a name for the module and create a file to describe the module. For example, we can call the file – (in my Drupal 7 is

Creating of an .info file
# Name of the module. 
name: My List
# Detailed description of the module.
description: Create page in drupal 8
# Indicate in which module group will our module be displayed on / admin / modules page.
package: custom
# Indicate that this module. Occurs (module, theme, profile).
type: module
# The key that defines the version of the kernel. Obligatory!!!
core: 8.x
# This specifies the rout's name (which is announced in the mylist.routing.yml file) for the 
configuration of the module.
configure: mylist.add_record
# Also can be the following values:
# dependencies - list of modules from which your module depends.
# test_dependencies - list of modules that will be included when performing automated tests.
# hidden: TRUE - the option hides this module from the list of modules.
# To show all the hidden modules in the list, you need to set 
$settings ['extension_discovery_scan_tests'] = TRUE in settings.php.

Creating of a .module file

In Drupal 7, the .module file was required, but in Drupal 8 it is optional, and it may not be. An example file of your own theme that data display we need.

<? php

/ **
* Implements hook_theme ().
* /
function mylist_theme () {
 return array (
   'mylist_theme' => array (// Subject name
     'variables' => array (// Variable names array
       'data' => array (),
     'templates' => 'templates / mylist-theme', // Template path without .html.twig, in which the 
variables listed above will be available.

In Drupal 8, unlike Drupal 7, you can not create a theme that will give html - everything lies in the TWIG template

Create a TWIG Template

Drupal 8 has changed the template. Instead of the usual PHPtemplate we use TWIG.

TWIG - is a compiler for open-source templates, written in the PHP programming language.

Since we specified the path 'templates / mylist-theme' in the theme, we create the folder templates in the root of the module folder and, accordingly, we create the mylist-theme.html.twig file, which, in fact, will lie our HTML.

 <div class = "row column text-center">

 <h2>{{data.title}} </h2>


 <p> {{data.body}} </p>


Here you can see the work with variable data, which was specified in hook_theme ().

Creating a src derivation

Next we need to create a subdirectory in the folder of our module, in which we will store controllers, plug-ins, forms, templates and tests. This subdirectory should be called src. This will allow the controller class to be added to the autoloader (PSR-4 automatically, respectively, manually connect nothing.

Create a table in the database

To create tables in Drupal 8, we use the same hook_shema ().

Actually, create a file at the root of the module mylist.install, which is responsible for the actions when installing or updating.

For a test, we will create a table with 3 fields, in which we will record data from the configuration of the form, the information about it we can read below.

Creating of a service

In the earlier versions of Drupal, all functions and methods were initialized at the page render regardless of whether we use them or not. This affected the speed of the program. So, in Drupal 8 there was such a concept as services. It is borrowed from Symphony and allows you to call the functions we use when writing a code.

To create the service, you need to create a file named, in which we describe the service itself.

// The name of the service.
  // The class that returns the service.
  // Since Drupal 8 uses the PSR-4 autoloader, we skip src.
  class: Drupal \ mylist \ MyListDbLogic
  // Arguments that arrive in the class constructor.
  argument: ['@database']
    - {name: backend_overridable}

This service is needed for us to work with the table created above. The argument @database gives you the ability to work with the database.

Call service in the code

Creating a routine

In Drupal 8 hook_menu was replaced by routings and took it all into a separate file. The file was named mylist.routing.yml.

// The name of the routing, it is used to generate links, redirects, and so on.
 // Way that will be on the site.
 path: '/ admin / mylist / add_record'
 // Page header
   _title: 'add record'
   // Displayed on the form page. An analogue of drupal_get_form
   _form: '\ Drupal \ mylist \ Form \ ConfigFormMyList'
 // Right
   _permission: 'access simple page'
 path: '/ mylist / {mylist_id}'
   _title: 'My list'
   // page callback. The method that renders our page.
   _controller: '\ Drupal \ mylist \ Controller \ MyListController :: content'
   _permission: 'view content'
   // You can use a regular for the correctness of the contents of an argument.
   // In this case, mylist_id variable will only be integer, otherwise  will be available.
   mylist_id: \ d +

Create a controller

Let's look at MVC, a bit of the theory:

Model - provides some information (data and methods of working with these data), responds to requests, changing its status. Does not contain information on how this knowledge can be visualized.

View - is responsible for displaying information (visualization). Often, as a representation, a form (window) with graphic elements acts.

Controller - provides communication between the user and the system: controls the data input by the user and uses the model and representation to implement the necessary response.

Create a file in the path /modules/mylis /src/ Controller and call it MyListController.php

<? php
/ **
* @file
* Contains \ Drupal \ mylist \ Controller \ MyListController.
* /
namespace Drupal \ mylist \ Controller;
use Drupal \ Core \ Controller \ ControllerBase;
use Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException;
class MyListController extends the ControllerBase {
 // The variable name is the same as in the router !!!
 public function content ($ mylist_id = NULL) {
   // Download the service.
   $ db_logic = \ Drupal :: service ('mylist.db_logic');
   if ($ record = $ db_logic-> getById ($ mylist_id, TRUE)) {
     return array (
       // Work with our theme.
       '#theme' => 'mylist_theme',
       '#data' => $ record
   // Return: page not found.
   throw new NotFoundHttpException ();

Working with config

To replace the variable_set / get came config. To create a variable, you must in /mypage / config / schema create the file configform_mylist.schema.yml and prescribe:

// Configuration name
 type: config_object
 label: 'Configform Example settings'
     type: string
     label: 'This is the example email address.'

Examples of work:
$config = $ this-> config ('configform_mylist.settings');
$config-> set ('email_address', 'test');
$config-> save ();

Create a Config Form

The creation of forms in Drupal 8 has fundamentally changed - now there are no hooks we have, but there are classes. So, we'll create a ConfigFormMyList.php file and place it in /modules/mylist/src/Form

<? php
/ **
* @file
* Contains \Drupal \mylist \Form \ConfigFormMyList.
* /
namespace Drupal \ mylist \ Form;
use Drupal \ Core \ Form \ ConfigFormBase;
use Drupal \ Core \ Form \ FormStateInterface;
use drupal \ core \ url;
use Drupal \ Component \ Utility \ SafeMarkup;
class ConfigFormMyList extends ConfigFormBase {
 / **
  * {@inheritdoc}.
  * /
 // The method that returns the form of the form.
 public function getFormId () {
   return 'configform_mylist_form';
 / **
  * {@inheritdoc}.
  * /
 // instead of hook_form.
 public function buildForm (array $ form, FormStateInterface $ form_state) {
   $ form = parent :: buildForm ($ form, $ form_state);
   $ config = $ this-> config ('configform_mylist.settings');
   $ form ['email'] = array (
     '#type' => 'email',
     '#title' => $ this-> t ('Your .com email address.'),
     '#default_value' => $ config-> get ('email_address'),
   $ form ['title'] = array (
     '#type' => 'textfield',
     '#title' => $ this-> t ('Title'),
     '#required' => TRUE,
   $ form ['body'] = array (
     '#type' => 'textarea',
     '#title' => $ this-> t ('Body'),
     '#rows' => 5
     '#required' => TRUE,
   $ db_logic = \ Drupal :: service ('mylist.db_logic');
   $ data = $ db_logic-> getAll ();
   if ($ date) {
     $ form ['data'] = array (
       '#type' => 'table',
       '#caption' => $ this-> t ('Table Data'),
       '#header' => array ($ this-> t ('id'), $ this-> t ('Title'), $ this-> t ('Body'))
     foreach ($ data as $ item) {
       // Example of creating a link.
       // The first argument specifies the name of the rout, and the second argument is its
       $ url = Url :: fromRoute ('mylist.view', array (
         'mylist_id' => $ item-> id
       $ form ['data'] [] = array (
         'id' => array (
           '#type' => 'markup',
           '#markup' => \ Drupal :: l ($ item-> id, $ url),
         'title' => array (
           '#type' => 'markup',
           '#markup' => $ item-> title,
         'body' => array (
           '#type' => 'markup',
           '#markup' => $ item-> body,
   return $ form;
 / **
  * {@inheritdoc}
  * /
 // instead of hook_form_validate.
 public function validateForm (array & $ form, FormStateInterface $ form_state) {
   if (strpos ($ form_state-> getValue ('email'), '.com') === FALSE) {
     $ form_state-> setErrorByName ('email', $ this-> t ('This is not a .com email address.'));
 / **
  * {@inheritdoc}
  * /
 // instead of hook_form_submit.
 public function submitForm (array & $ form, FormStateInterface $ form_state) {
   $ db_logic = \ Drupal :: service ('mylist.db_logic');
   $ title = SafeMarkup :: checkPlain ($ form_state-> getValue ('title'));
   $ body = SafeMarkup :: checkPlain ($ form_state-> getValue ('body'));
   $ db_logic-> add ($ title, $ body);
   // replaced config with variable_set / get.
   // Example of working with them.
   $ config = $ this-> config ('configform_mylist.settings');
   $ config-> set ('email_address', $ form_state-> getValue ('email'));
   $ config-> save ();
   return parent :: submitForm ($ form, $ form_state);
 / **
  * {@inheritdoc}
  * /
 // The array of configuration object names that are available for editing.
 protected function getEditableConfigNames () {
   return ['configform_mylist.settings'];


Add a link to the menu item

Add a link in the admin menu - for this we need to create a file at the root of the module and place the following there:

// Headline
title: 'Add content for mylist'
// System menu name
parent: system.admin
// The name of the rout.
route_name: mylist.add_record

The result should be the following file structure:
• custom
• src
• Form
• templates

This is all - our module is ready!
Quick Free Quote
We respect your privacy. NO SPAM No selling your personal data.
We will respond to your query & collect further details within 24 hours. Guaranteed!


We are friendly people who love to talk. So go ahead and contact us.


Awards & Certification