| name | drupal-hooks |
| description | Use this skill when implementing Drupal hooks, altering forms, modifying entities, or extending Drupal behavior. Trigger phrases include "hook", "alter", "form alter", "entity presave", "preprocess", "event subscriber". |
| allowed-tools | Read, Write, Edit, Glob, Grep |
Drupal Hooks Skill
Comprehensive knowledge of Drupal 10/11 hooks and event subscribers.
Hook Implementation
Hooks are implemented in <module>.module files:
<?php
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\NodeInterface;
Common Hooks
Form Hooks
function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
}
function mymodule_form_user_login_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['name']['#description'] = t('Enter your email address.');
}
Entity Hooks
function mymodule_entity_presave(EntityInterface $entity) {
}
function mymodule_node_presave(NodeInterface $node) {
if ($node->bundle() === 'article') {
}
}
function mymodule_entity_insert(EntityInterface $entity) {
}
function mymodule_entity_update(EntityInterface $entity) {
}
function mymodule_entity_delete(EntityInterface $entity) {
}
function mymodule_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
}
function mymodule_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
}
Access Hooks
function mymodule_node_access(NodeInterface $node, $op, AccountInterface $account) {
if ($node->bundle() === 'private' && $op === 'view') {
return AccessResult::forbidden()->cachePerUser();
}
return AccessResult::neutral();
}
function mymodule_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
return AccessResult::neutral();
}
View Hooks
function mymodule_views_pre_render(ViewExecutable $view) {
if ($view->id() === 'my_view') {
}
}
function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
}
function mymodule_views_data() {
$data = [];
$data['mymodule_table']['table']['group'] = t('My Module');
$data['mymodule_table']['table']['base'] = [
'field' => 'id',
'title' => t('My Module Data'),
];
return $data;
}
function mymodule_views_data_alter(array &$data) {
}
Theme Hooks
function mymodule_theme($existing, $type, $theme, $path) {
return [
'mymodule_item' => [
'variables' => [
'title' => NULL,
'content' => NULL,
'attributes' => [],
],
],
];
}
function mymodule_preprocess_node(&$variables) {
$node = $variables['node'];
$variables['custom_class'] = 'node-' . $node->bundle();
}
function mymodule_theme_suggestions_node_alter(array &$suggestions, array $variables) {
$node = $variables['elements']['#node'];
$suggestions[] = 'node__' . $node->bundle() . '__custom';
}
Mail Hooks
function mymodule_mail($key, &$message, $params) {
switch ($key) {
case 'notification':
$message['subject'] = t('Notification');
$message['body'][] = $params['message'];
break;
}
}
function mymodule_mail_alter(&$message) {
}
Cron Hooks
function mymodule_cron() {
}
Install/Update Hooks
function mymodule_install() {
}
function mymodule_uninstall() {
}
function mymodule_update_10001(&$sandbox) {
}
function mymodule_requirements($phase) {
$requirements = [];
if ($phase === 'runtime') {
$requirements['mymodule'] = [
'title' => t('My Module'),
'value' => t('Configured'),
'severity' => REQUIREMENT_OK,
];
}
return $requirements;
}
Menu/Routing Hooks
function mymodule_menu_links_discovered_alter(&$links) {
}
function mymodule_local_tasks_alter(&$local_tasks) {
}
Page Hooks
function mymodule_page_attachments(array &$attachments) {
$attachments['#attached']['library'][] = 'mymodule/global';
}
function mymodule_page_attachments_alter(array &$attachments) {
}
Event Subscribers (Preferred in Drupal 10/11)
Modern Drupal prefers event subscribers over hooks for many use cases.
Service Definition
In mymodule.services.yml:
services:
mymodule.event_subscriber:
class: Drupal\mymodule\EventSubscriber\MyEventSubscriber
tags:
- { name: event_subscriber }
Event Subscriber Class
<?php
namespace Drupal\mymodule\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class MyEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
KernelEvents::REQUEST => ['onRequest', 100],
KernelEvents::RESPONSE => ['onResponse', 0],
];
}
public function onRequest(RequestEvent $event) {
}
public function onResponse(ResponseEvent $event) {
}
}
Common Events
| Event | Class | Use Case |
|---|
KernelEvents::REQUEST | RequestEvent | HTTP request handling |
KernelEvents::RESPONSE | ResponseEvent | Before response sent |
KernelEvents::EXCEPTION | ExceptionEvent | Exception handling |
KernelEvents::TERMINATE | TerminateEvent | After response sent |
ConfigEvents::SAVE | ConfigCrudEvent | Config saved |
ConfigEvents::DELETE | ConfigCrudEvent | Config deleted |
EntityHookEvents::ENTITY_PRESAVE | EntityEvent | Entity presave (D11) |
Config Event Subscriber
<?php
namespace Drupal\mymodule\EventSubscriber;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ConfigSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
ConfigEvents::SAVE => ['onConfigSave'],
];
}
public function onConfigSave(ConfigCrudEvent $event) {
$config = $event->getConfig();
if ($config->getName() === 'mymodule.settings') {
}
}
}
Hook Attributes (Drupal 11+)
Drupal 11 introduces hook attributes as an alternative to procedural hooks:
<?php
namespace Drupal\mymodule\Hook;
use Drupal\Core\Hook\Attribute\Hook;
class MyModuleHooks {
#[Hook('form_alter')]
public function formAlter(&$form, $form_state, $form_id): void {
}
#[Hook('cron')]
public function cron(): void {
}
}
Finding Existing Hooks
grep -r "function mymodule_.*_form_alter" modules/custom/
grep -E "^function <module>_" modules/custom/<module>/<module>.module
grep -r "EventSubscriberInterface" modules/custom/
Hook Weight
Control execution order via module weight in the system.module table or by implementing hook_module_implements_alter():
function mymodule_module_implements_alter(&$implementations, $hook) {
if ($hook === 'form_alter') {
$group = $implementations['mymodule'];
unset($implementations['mymodule']);
$implementations['mymodule'] = $group;
}
}