Drupal is a really powerful CMS and it offers a lot of extendibility out of the box, mainly thanks to Symfony. One of the basic tasks we can archive is the creation of custom endpoints associated to a controller, in addition to the system ones generated by the definition and creation of content. The controller can then provide a response in different formats (HTML markup, JSON, Twig, etc.).
For the first step we need to create a new module and define a MY_MODULE.routing.yml file in which specify the url, the name of the controller and the access permissions.
Module creation
We use drupal
console command in order to create a new module:
drupal generate:module
After answering the questions (use all the defaults, except the unit test class creation), these files will be generated in the folder modules/custom/MACHINE_NAME_MY_MODULE (composer.json is optional):
├── MACHINE_NAME_MY_MODULE.info.yml
├── MACHINE_NAME_MY_MODULE.module
├── composer.json
└── templates
└── MACHINE_NAME_MY_MODULE.html.twig
It is also possible to run the command without wizard by passing directly the options. Refer to the guide generate:module | Drupal Console
Module installation
At this point we install the new module with the command:
drupal module:install MACHINE_NAME_MY_MODULE
Alternatively we can use the Drupal admin UI at /admin/modules.
Controller creation
To create the controller associated to the module, use the command:
drupal generate:controller
Answer the questions:
- module name (enter the one previously created)
- controller class name
- controller method title
- action method name
- route path (related to the point 4)
Points from 3 to 5 are repeated until you leave a blank method title (so you can add several methods/routes in one shot).
At the end of the procedure these files will be generated:
├── MACHINE_NAME_MY_MODULE.routing.yml
└── src
└── Controller
└── CONTROLLER_NAME.php
Also in this case it is possible to run the command without a wizard.
Refer to the guide generate:controller | Drupal Console
Output data with Twig
If you visit the url set in the controller you will receive as response the default output generated by the generate:controller
command.
Implement method: METHOD_NAME
To render the Twig template already present in the templates subdirectory of the module, you have to modify the method created in the controller like this (in this example the method name created in the previous step is content)
from this:
public function content() {
return [
'#type' => 'markup',
'#markup' => $this->t('Implement method: content')
];
}
to this:
public function content() {
return [
'#theme' => 'MACHINE_NAME_MY_MODULE'
];
}
Passing variables to the template
If you want to pass variables from the controller to the Twig template, you must edit the MACHINE_NAME_MY_MODULE.module file to list the variables you want to use, for example like this:
function MACHINE_NAME_MY_MODULE_theme() {
return [
'MACHINE_NAME_MY_MODULE' => [
'render element' => 'children',
'variables' => [
'var1' => '',
'items' => []
]
],
];
}
Then in the controller content method we go to value the two variables var1 and items:
public function content() {
$var1 = 'Hello world!';
$array1 = [1, 2, 3, 4];
return [
'#theme' => 'MACHINE_NAME_MY_MODULE',
'#var1' => $var1,
'#items' => $array1,
];
}
Finally clean up the cache with the command:
drupal cc
Now we have access to the var1 and items variables directly in the Twig template, as in the following example:
<div>
<p>{{ var1 }}</p>
{% for item in items %}
{{ item }}
{% endfor %}
</div>
Change access role
Using the controller generation command, default access is allowed to all users via this rule in the MACHINE_NAME_MY_MODULE.routing.yml file:
requirements:
_permission: 'access content'
For example, if we want to restrict access to authenticated users only, we have to edit the content of the file as follows:
requirements:
_permission: 'access content'
_role: 'authenticated'
It is possible to specify multiple values using "," as a separator if you want to use AND logic, otherwise use "+" for OR logic, as in the following example:
requirements:
_permission: 'access content'
_role: 'authenticated, editor'
Change caching option
Starting from Drupal 8.9, the output of the views of a custom module is automatically cached. If you want to disable this behavior, for example if your content is dynamic, add the no_cache option in the MY_MODULE.routing.yml file:
requirements:
_permission: 'access content'
_role: 'authenticated'
options:
no_cache: TRUE
Top comments (1)
Great post! When it comes to adding additional routes using a custom module in Drupal 8/9, it can significantly extend the functionality of a site by creating new paths and custom controllers. If anyone is looking to enhance their Drupal platform or needs expert help with custom Drupal development, check out Custom Drupal Development for tailored solutions and top-tier development services in the USA