mcpField
D
drupal-hooks

drupal-hooks

github.com/edutrul开发7351 次安装21 周前更新
代码仓库

关于 drupal-hooks

面向 Drupal 模块开发者的 Drupal 11 Hook 开发参考,优先介绍 `src/Hook/` 中可测试的面向对象 Hook,也说明过程式 Hook 及事件订阅器的适用场景。

主要功能

  1. 01通过属性定义面向对象 Hook
  2. 02使用构造函数依赖注入
  3. 03利用 Drupal 11.1+ 自动注册
  4. 04为 `src/Hook/` 外的 Hook 手动注册
  5. 05比较 Hook 与事件订阅器

适用场景

  1. 01实现新的 Drupal 模块 Hook
  2. 02将过程式 Hook 重构为类
  3. 03选择 Hook 或事件订阅器

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/edutrul/drupal-ai --skill drupal-hooks

安装后在 Claude Code 中输入/drupal-hooks确认已加载。

附带文件README.md

详细介绍

Drupal Hooks(Drupal 11)

OOP Hooks(首选)

位置src/Hook/MyModuleHooks.php
命名空间Drupal\my_module\Hook
自动注册Drupal 11.1+(无需 services.yml)
DI 支持构造函数注入
可测试是 — 可直接实例化并注入模拟对象

示例(最佳实践)

<?php

namespace Drupal\my_module\Hook;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\node\NodeInterface;

final class MyModuleHooks {

  use StringTranslationTrait; // Provides $this->t().

  public function __construct(
    private readonly AccountProxyInterface $currentUser,
  ) {}

  /**
   * Implements hook_form_node_article_form_alter().
   *
   * Prefer targeted form hooks over generic form_alter — they only fire for
   * the specific form ID and avoid unnecessary processing.
   */
  #[Hook('form_node_article_form_alter')]
  public function formNodeArticleFormAlter(array &$form, FormStateInterface $form_state): void {
    $form['title']['#title'] = $this->t('Article title');
  }

  /**
   * Implements hook_form_alter().
   *
   * Use only when acting on multiple forms or the form ID is unknown at
   * development time. Check $form_id explicitly.
   */
  #[Hook('form_alter')]
  public function formAlter(array &$form, FormStateInterface $form_state, string $form_id): void {
    if ($form_id === 'node_page_form') {
      $form['title']['#description'] = $this->t('Enter a descriptive page title.');
    }
  }

  #[Hook('node_presave')]
  public function nodePresave(NodeInterface $node): void {
    if ($node->getType() === 'article') {
      // Example: stamp the current user's ID on save.
      $node->set('uid', $this->currentUser->id());
    }
  }

  #[Hook('theme')]
  public function theme(): array {
    return [
      'my_template' => [
        'variables' => ['content' => NULL],
      ],
    ];
  }

}

注意: 如果钩子未执行,请检查命名空间和位置。src/Hook/ 之外的 OOP 钩子需要手动注册服务。

services.yml

场景是否需要?
类位于 src/Hook/,命名空间为 Drupal\my_module\Hook否 — 自动注册(Drupal 11.1+)
类位于 src/Hook/ 之外(例如自定义服务)是 — 手动注册

需要注册时,autowire 会根据类型提示解析构造函数依赖项:

namespace Drupal\my_module\Service;

use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Session\AccountProxyInterface;

final class MyCustomService {

  public function __construct(
    private readonly AccountProxyInterface $currentUser,
  ) {}

  #[Hook('form_alter')]
  public function formAlter(...): void {
    // ...
  }

}
services:
  Drupal\my_module\Service\MyCustomService:
    autowire: true

程序式钩子(.module)

通过 my_module_hook_name() 命名方式自动发现。仍然有效;新代码优先使用 OOP 钩子。

钩子与事件订阅器

使用钩子的场景使用事件订阅器的场景
form_alter、实体钩子、主题钩子响应已分发的 Symfony 事件
扩展 Drupal 核心/贡献模块的行为PSR-14 / 解耦系统
使用现有 Drupal API构建松耦合逻辑

src/Hook/ 中的 OOP 钩子完全可测试 — 可直接实例化类并注入模拟依赖项。

规则(重要)

  • 对于新的实现,始终优先在 src/Hook/ 中使用 OOP 钩子
  • 除非明确要求,否则绝不要在 .module 中添加新钩子
  • 如果存在程序式钩子,建议将其重构为 OOP

评论

支持 Markdown