Preparing for class-based hooks in Drupal: a brief migration guide
maandag 7 juli 2025 - 686 woorden, 4 min read
Learn how to update your Drupal modules by migrating traditional hooks to the new class-based hook system which is a potential requirement for Drupal 12.
Migrating Drupal hooks to class Hooks for Drupal 12 Compatibility
Important note on Drupal 12 requirements
Before diving into the migration process, it’s important to note that as of now (July 2025), there is ongoing discussion in the Drupal community about whether this migration will be mandatory for Drupal 12. The issue Release Drupal 12 in 2026 is under active discussion, and no final decision has been made regarding the deprecation of traditional hook implementations.
While migrating to class-based hooks is considered a best practice, you may want to monitor the linked issue for future announcements.
The transition from traditional hook implementations in .module
files to class-based hooks represents a modernization effort that brings Drupal’s hook system more in line with modern PHP practices and object-oriented programming principles. Whether or not it becomes a requirement for Drupal 12, understanding and implementing this approach can improve your codebase’s maintainability and structure.
The changes
Traditionally, Drupal hooks were implemented as functions in .module
files, following the pattern hook_*
or modulename_*
. For example:
function mymodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
// Form alteration logic here
}
In Drupal 12, these hooks need to be implemented as classes using PHP attributes. This change provides better structure, improved IDE support, and makes the code more maintainable.
The Class-based hook structure
The new hook system uses PHP classes with attributes to define hooks. Here’s the basic structure:
namespace Drupal\mymodule\Hook;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
class FormHooks {
#[Hook('form_alter')]
public function formAlter(&$form, FormStateInterface $form_state, $form_id): void {
// Form alteration logic here
}
}
Key components of the new structure:
- Classes are placed in the module’s
src/Hook
directory - The
#[Hook]
attribute defines which hook is being implemented - Methods can have any name, but should be descriptive
- Type hints and return types are encouraged for better code quality
Step-by-Step Migration Guide
1. Create the Hook Directory
First, create a new directory structure in your module:
mymodule/
├── src/
│ └── Hook/
└── mymodule.module
2. Create Hook Classes
Group related hooks into classes. For example, create separate classes for entity hooks, form hooks, etc.
// src/Hook/EntityHooks.php
namespace Drupal\mymodule\Hook;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Hook\Attribute\Hook;
class EntityHooks {
#[Hook('entity_presave')]
public function onEntityPresave(EntityInterface $entity): void {
// Entity presave logic
}
#[Hook('entity_view')]
public function onEntityView(array &$build, EntityInterface $entity, $display, $view_mode): void {
// Entity view logic
}
}
3. Migrate form hooks
Form-related hooks can be grouped in a FormHooks class:
// src/Hook/FormHooks.php
namespace Drupal\mymodule\Hook;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
class FormHooks {
#[Hook('form_alter')]
public function alterForms(&$form, FormStateInterface $form_state, $form_id): void {
// Generic form alter logic
}
#[Hook('form_node_form_alter')]
public function alterNodeForms(&$form, FormStateInterface $form_state): void {
// Node-specific form alter logic
}
}
4. Update your .module File
Once you’ve migrated your hooks to classes, you can remove the old hook implementations from your .module
file. The hook system will automatically discover and use the class-based implementations.
Best Practices and Tips
-
Organize Logically: Group related hooks into meaningful classes. For example:
EntityHooks.php
for entity-related hooksFormHooks.php
for form-related hooksThemeHooks.php
for theme-related hooks
- Use Type Hints: Always use proper type hints and return types for better code quality and IDE support:
class ThemeHooks {
#[Hook('theme')]
public function registerThemes(): array {
return [
'my_custom_theme' => [
'variables' => ['content' => NULL],
],
];
}
}
- Method Naming: Choose descriptive method names that indicate the hook’s purpose:
class BlockHooks {
#[Hook('block_view_alter')]
public function modifyBlockViewContent(array &$build, BlockPluginInterface $block): void {
// Block view modification logic
}
}
Conclusion
Migrating to class-based hooks can become a necessary step for Drupal 12 compatibility. While it requires some initial effort to restructure your code, the benefits include:
- Better code organization
- Improved IDE support and code completion
- More maintainable and testable code
- Alignment with modern PHP practices
For more detailed information, you can refer to:
Sections of this article are created with the assistant of Goose (AI agent) and Claude (LLM).