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.
Table of Contents
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’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.
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.
3. Dependency Injection
Class-based hooks can leverage services via Dependency Injection, which is not possible in procedural hooks.
4. Modern Codebase
This change modernizes Drupal, making it more attractive to developers familiar with Symfony, Laravel, or other OOP-centric frameworks.
How to Get Started
- Make sure you’re on Drupal 11.1 or newer.
- Create a PHP class that implements a valid hook interface.
- Register it if needed in
mymodule.services.yml
. - Clear the cache.
- Test the hook behavior thoroughly.
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.