How to Create a Custom Drupal Module from Scratch
Backend Development
  • 22 Views

How to Create a Custom Drupal Module from Scratch

Creating a custom Drupal module allows you to extend the functionality of your Drupal site in a clean, modular, and maintainable way. Modules can add features such as custom content types, APIs, forms, or integrations with external services. In this post, we will go step by step to build a custom module from scratch, explaining how to structure files, define hooks, create routes, controllers, forms, and implement best practices for maintainability and performance.

Understanding Drupal Module Structure

A Drupal module consists of a folder containing at least an `.info.yml` file and optionally other files such as `.module`, routing, and controllers. The `.info.yml` file provides metadata, including module name, type, description, core version, and dependencies.

1name: CustomModule
2type: module
3description: 'A custom module for Drupal'
4core_version_requirement: ^9 || ^10
5package: Custom
Module Structure Key Points:
  • Folder inside /modules/custom/module_name
  • Required info.yml file for metadata
  • Optional module_name.module for hooks
  • Other optional files: routing.yml, Controller.php, Form.php

Creating the Module Folder and Files

Create a folder under `/modules/custom/custom_module` and add essential files such as `custom_module.info.yml` and `custom_module.module`. Additional files like controllers, forms, and services can be added as needed.

1mkdir -p modules/custom/custom_module
2cd modules/custom/custom_module
3touch custom_module.info.yml custom_module.module
Tips for File Organization:
  • Use lowercase and underscores for file/folder names
  • Keep related files organized in subfolders
  • Plan modular structure for scalability

Defining Routes and Controllers

For creating pages or API endpoints, define routes in `custom_module.routing.yml` and associate them with controllers. Controllers handle the business logic and return content, forms, or JSON responses.

1custom_module.content:
2  path: '/custom'
3  defaults:
4    _controller: '\Drupal\custom_module\Controller\CustomController::content'
5    _title: 'Custom Module Page'
6  requirements:
7    _permission: 'access content'
Routing Tips:
  • Use meaningful route paths and names
  • Set proper permissions for security
  • Keep controllers focused and modular

Creating a Controller

Controllers define the logic for rendering pages or returning data. They are placed under `src/Controller` inside your module folder. Use the `ControllerBase` class for convenience and return render arrays or JSON responses.

1namespace Drupal\custom_module\Controller;
2
3use Drupal\Core\Controller\ControllerBase;
4
5class CustomController extends ControllerBase {
6  public function content() {
7    return [
8      '#type' => 'markup',
9      '#markup' => $this->t('Hello, this is a custom module page!'),
10    ];
11  }
12}
Controller Best Practices:
  • Keep controllers focused on a single responsibility
  • Return render arrays for HTML content
  • Return JSON for API endpoints
  • Use dependency injection for services

Adding Hooks

Drupal hooks allow modules to interact with core or other modules. Common hooks include `hook_help()`, `hook_form_alter()`, and `hook_entity_insert()`. Hooks should be implemented in your `.module` file.

1function custom_module_help($route_name, $route_match) {
2  switch ($route_name) {
3    case 'help.page.custom_module':
4      return '<p>' . t('Help text for Custom Module.') . '</p>';
5  }
6}
Hook Tips:
  • Use hooks to extend core functionality without modifying core files
  • Document each hook clearly
  • Avoid heavy logic inside hooks, delegate to services if possible

Creating Forms

Custom forms allow users to input data, which can then be saved to the database or processed. Use Form API classes extending `FormBase` and define form elements, validation, and submission logic.

1namespace Drupal\custom_module\Form;
2
3use Drupal\Core\Form\FormBase;
4use Drupal\Core\Form\FormStateInterface;
5
6class CustomForm extends FormBase {
7  public function getFormId() { return 'custom_form'; }
8  public function buildForm(array $form, FormStateInterface $form_state) {
9    $form['name'] = [
10      '#type' => 'textfield',
11      '#title' => $this->t('Name'),
12      '#required' => TRUE,
13    ];
14    $form['submit'] = ['#type' => 'submit', '#value' => $this->t('Submit')];
15    return $form;
16  }
17  public function submitForm(array &$form, FormStateInterface $form_state) {
18    Drupal::messenger()->addMessage($this->t('Hello, @name', ['@name' => $form_state->getValue('name')]));
19  }
20}
Form Tips:
  • Use Form API for proper Drupal integration
  • Validate user input for security
  • Provide clear feedback messages
  • Keep form logic modular for reuse

Best Practices and Recommendations

To create maintainable Drupal modules, follow coding standards, use services for reusable logic, keep functionality modular, and document your code. Avoid hardcoding values, leverage Drupal APIs, and test your module thoroughly.

Recommendations:
  • Follow Drupal coding standards and PSR-12
  • Use services and dependency injection for reusable code
  • Document all hooks, routes, and controllers
  • Test modules in development before production
  • Keep modules lightweight and focused on a single responsibility

Conclusion

Creating a custom Drupal module from scratch provides powerful flexibility to extend your website's functionality. By following best practices, structuring files correctly, using controllers, hooks, forms, and services, you can build robust, maintainable, and scalable modules that enhance your Drupal project.

Key Takeaways:
  • Understand the module folder and file structure
  • Use routes, controllers, and hooks effectively
  • Implement forms with Form API for user interaction
  • Follow best practices for maintainable code
  • Test thoroughly and document for collaboration

References

Helpful resources for Drupal module development:

Reference Links: