Hooks as Classes in Drupal 11: A Modern Developer’s Guide

With the release of Drupal 11, one of the most exciting changes for developers is the ability to write hooks as classes. This shift towards object-oriented PHP makes your codebase more maintainable, testable, and modern.

In this blog post, we’ll explore this new approach using a practical example: rewriting the hook_entity_presave hook as a class.

What Are Hooks in Drupal?

Hooks are functions that allow modules to interact with or alter Drupal’s behavior. They’re key to Drupal’s modular architecture.

For example, the hook_entity_presave allows you to modify an entity just before it is saved to the database.

function mymodule_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  // Perform some action before an entity is saved.
  if ($entity->getEntityTypeId() === 'node') {
    $entity->set('title', strtoupper($entity->getTitle()));
  }
}

Traditionally, hooks have been procedural, which works but often leads to fragmented and hard-to-test codebases. As PHP and modern development practices evolve, Drupal is now aligning with object-oriented programming (OOP) standards.

🔗 What are hooks? – Drupal Documentation


What’s New in Drupal 11?

As of Drupal 11.1, you can now write many hooks as methods in PHP classes. This means cleaner code, better structure, and more powerful integrations using services and dependency injection.

This change marks a shift toward more Symfony-style development, and that’s a win for long-term maintainability.

🔗 Drupal 11.1.0 Release Notes


Before vs. After: Hooks as Classes Implementation

Converting hook_entity_presave to a Class-Based Hook

Old Procedural Hook (Drupal 10 and earlier)

function mymodule_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
  // Perform some action before an entity is saved.
  if ($entity->getEntityTypeId() === 'node') {
    $entity->set('title', strtoupper($entity->getTitle()));
  }
}

Object-Oriented (New Way):

namespace Drupal\mymodule\Hook;

use Drupal\Core\Entity\EntityInterface;
use Drupal\hook_event_dispatcher\Hook\Entity\EntityPresaveHookInterface;

class EntityPresaveHook implements EntityPresaveHookInterface {

  public function presave(EntityInterface $entity): void {
    if ($entity->getEntityTypeId() === 'node') {
      $entity->set('title', strtoupper($entity->getTitle()));
    }
  }
}

🧩 You may need to register this class in services.yml depending on your hook.


Why This Change Matters

1. Cleaner Code

No more hunting for procedural functions in different files—everything is neatly contained in classes.

2. Better Testability

Classes can be tested using unit tests, mocking dependencies like services or configuration.

🔗 Testing in Drupal – Official Guide

3. Dependency Injection

Class-based hooks can leverage services via Dependency Injection, which is not possible in procedural hooks.

🔗 Services and Dependency Injection in Drupal

4. Modern Codebase

This change modernizes Drupal, making it more attractive to developers familiar with Symfony, Laravel, or other OOP-centric frameworks.

🔗 Drupal & Symfony Integration


How to Get Started

  1. Make sure you’re on Drupal 11.1 or newer.
  2. Create a PHP class that implements a valid hook interface.
  3. Register it if needed in mymodule.services.yml.
  4. Clear the cache.
  5. Test the hook behavior thoroughly.

🔗 How to Define Services in Drupal


Are All Hooks Supported?

Not all hooks are supported yet. However, the list is expanding and you can check ongoing updates in the Drupal.org issue tracker.


Final Thoughts

The ability to write hooks as classes is a major enhancement that aligns Drupal with modern PHP development. It improves structure, encourages good coding practices, and supports better test coverage.

If you’re still using procedural hooks, this is your sign to start refactoring. Even converting a few hooks in your custom module can significantly improve clarity and flexibility.

If you’re looking to modernize your codebase and need help converting procedural hooks, feel free to connect—we’d love to help you get started.

Leave a Reply

Your email address will not be published. Required fields are marked *

×