Creating a custom controller
In Drupal, a controller is responsible for receiving a request and returning a response usually in the form of a Render Array. In this tutorial, we will build a module that displays a simple message and a data table at a custom URL.
Step 1: Define the Module
This step can be skipped if you are defining a controller in an existing module. We need to tell Drupal that our module exists. Create a folder named mint_controller (this is an example, use the the machine name suitable for your moudule) and add the info.yml file. This file provides the metadata for the Drupal extension.
1name: 'Mint Controller Example'
2type: module
3description: 'Example module with custom controller'
4package: 'Mint'
5core_version_requirement: ^10 || ^11Step 2: Register the Route
Routes map a URL path to a specific PHP function or class method. We define our route in a routing file.
File: mint_controller.routing.yml(or whatever your module name was but the file name should end with .routing.yml
- Path: This is the URL (
/mint/example) where your controller will live. - _controller: This points to the PHP class that handles the logic. Note that we are using the full namespace.
- _permission: We use
access contentto ensure the page is viewable by general visitors.
1mint_controller.example:
2 path: '/mint/example'
3 defaults:
4 _title: 'Example Controller'
5 _controller: '\Drupal\mint_controller\Controller\ExampleController'
6 requirements:
7 _permission: 'access content'Step 3: Build the Controller Logic
Now we create the logic. Drupal uses PSR-4 autoloading, so the file must be placed in the src/Controller directory.
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\mint_controller\Controller;
6
7use Drupal\Core\Controller\ControllerBase;
8
9/**
10 * Returns responses for Mint Controller Example routes.
11 */
12class ExampleController extends ControllerBase {
13 /**
14 * Builds the response.
15 */
16 public function __invoke(): array {
17 $header = [
18 'id' => $this->t('ID'),
19 'name' => $this->t('Name'),
20 'email' => $this->t('Email'),
21 'status' => $this->t('Status'),
22 ];
23
24 $table = [
25 '#type' => 'table',
26 '#header' => $header,
27 '#rows' => $this->getRows(),
28 '#empty' => $this->t('No data available'),
29 ];
30
31 $build['content'] = [
32 '#type' => 'item',
33 '#markup' => $this->t('It works!'),
34 ];
35
36 $build['table'] = $table;
37
38 return $build;
39 }
40
41 public function getRows(): array {
42 return [
43 [1, 'Alice Smith', '[email protected]', 'Active'],
44 [2, 'Bob Jones', '[email protected]', 'Inactive'],
45 [3, 'Carol Lee', '[email protected]', 'Active'],
46 [4, 'David Kim', '[email protected]', 'Pending'],
47 [5, 'Emma Brown', '[email protected]', 'Active'],
48 [6, 'Frank White', '[email protected]', 'Inactive'],
49 [7, 'Grace Hall', '[email protected]', 'Active'],
50 [8, 'Henry King', '[email protected]', 'Pending'],
51 [9, 'Ivy Scott', '[email protected]', 'Active'],
52 [10, 'Jack Turner', '[email protected]', 'Inactive'],
53 ];
54 }
55}In this example, we use the __invoke() magic method. This allows the routing system to call the class directly as if it were a function.
Key Components of the Code:
- Inheritance: We extend
ControllerBase, which gives us access to helpful tools like the translation service ($this->t()). - The Header: We define an associative array for the table columns.
- The Render Array: Instead of returning raw HTML, we return a structured array.
#type => 'table': Tells Drupal to pass this data through its table theme template.#rows: The actual data fetched from thegetRows()helper method.
- Translation: All strings are wrapped in
$this->t()to ensure the interface can be translated into other languages.
Step 4: Enable and Test
Once your files are in place, follow these steps to see the result:
- Enable the module: Run
drush en mint_controlleror enable it via the/admin/modulesUI. - Clear Caches: Since we added a routing file, you must clear the cache for Drupal to "see" the new URL. Run
drush cr. - Visit the Page: Navigate to
your-site.com/mint/example.
Summary of Folder Structure
Your module structure should look like this:
mint_controller/
├── mint_controller.info.yml
├── mint_controller.routing.yml
└── src/
└── Controller/
└── ExampleController.php