Drupal 11 cambia las reglas: hooks orientados a objetos

hooks_drupal11
Solucionex
17
Abr 26

Drupal 11 introduce una mejora significativa en la forma de implementar hooks, permitiendo definirlos mediante clases y siguiendo principios de programación orientada a objetos.

Este nuevo enfoque convive con el sistema tradicional, lo que permite mantener compatibilidad con código legacy y realizar una migración progresiva sin necesidad de reescribir todo el proyecto de golpe.

Este cambio no solo moderniza la sintaxis, sino que también mejora la organización del código, facilita la inyección de dependencias y permite una arquitectura más mantenible y escalable.

El problema de los hooks tradicionales

function mymodule_node_insert(\Drupal\node\NodeInterface $node) {
  \Drupal::logger('mymodule')->notice('Nodo creado: ' . $node->id());
}

Funcionan bien, pero en proyectos reales suelen acabar siendo difíciles de testear, con lógica desordenada y fuertemente acoplados a servicios globales.

La nueva forma: hooks en clases (OOP)

namespace Drupal\mymodule\Hook;

use Drupal\Core\Hook\Attribute\Hook;
use Drupal\node\NodeInterface;

class NodeHooks {

  #[Hook('node_insert')]
  public function onInsert(NodeInterface $node) {
    \Drupal::logger('mymodule')->notice('Nodo creado: ' . $node->id());
  }

}

¿Dónde va este código?

La estructura recomendada dentro de un módulo custom es:

modules/custom/mymodule/src/Hook/NodeHooks.php

Detalles importantes:

  • Carpeta: src/Hook/
  • Namespace: Drupal\mymodule\Hook
  • Clase: libre (NodeHooks, FormHooks, etc.)

Drupal autodetecta estas clases como servicios gracias a los atributos, por lo que en muchos casos no es necesario registrarlas manualmente.

Reutilización de lógica

Una de las ventajas más interesantes es la posibilidad de reutilizar lógica fácilmente añadiendo múltiples atributos.

#[Hook('node_insert')]
#[Hook('node_update')]
public function onSave(NodeInterface $node) {
  // lógica común
}

Esto reduce duplicación y centraliza comportamiento relacionado.

Legacy Hooks

Durante la transición a hooks basados en clases en Drupal 11, se recomienda mantener un hook procedimental “shim” en el archivo .module. Esto permite empezar a usar hooks OOP incluso si el módulo aún no está desplegado en Drupal 11.1.

En lugar de duplicar lógica, el hook tradicional puede delegar directamente en la clase OOP correspondiente, lo que simplifica la migración y mantiene compatibilidad.

use Drupal\node\NodeInterface;

#[LegacyHook]
function services_hooks_example_node_insert(NodeInterface $node) {
  \Drupal::service('services_hooks_example.node_hooks')->nodeInsert($node);
}

El atributo #[LegacyHook] indica a Drupal que este hook es legado, evitando su ejecución si ya existe una implementación OOP equivalente.

Esto permite mantener el comportamiento actual mientras se habilita la testabilidad de la lógica en la nueva arquitectura. Cuando ya no sea necesario el soporte legacy, basta con eliminar estos hooks sin reescribir lógica adicional.

Si no necesitas soporte legacy, puedes mejorar el rendimiento desactivando el escaneo de hooks procedimentales desde el archivo .services.yml:

parameters:
  services_hooks_example.skip_procedural_hook_scan: true

Esto evita que Drupal escanee, registre o ejecute hooks legacy, reduciendo carga y mejorando rendimiento.

Importante 

no debes desactivar este soporte si tu módulo necesita compatibilidad con Drupal < 11.0.0.

 

Drupal 11 no elimina los hooks tradicionales, pero introduce una forma más estructurada y moderna de trabajarlos.

Este enfoque mejora la organización del código, la testabilidad, la reutilización de lógica y la transición desde legacy sin romper nada.

En la práctica, no es solo un cambio de sintaxis, sino una evolución hacia una arquitectura más limpia y mantenible.

Drupal
Drupal 11