mcpField
D
drupal-backend

drupal-backend

github.com/omedia开发28104 次安装53 周前更新
代码仓库

关于 drupal-backend

drupal-backend 是面向 Drupal 8 至 11+ 后端开发的技能,提供自定义模块、Drupal API、钩子、插件、服务、路由、表单和数据库操作的模式与示例。

主要功能

  1. 01自定义模块脚手架与结构
  2. 02钩子、控制器与路由
  3. 03表单与实体操作
  4. 04插件、服务与依赖注入
  5. 05数据库查询与架构定义
  6. 06开发与故障排查指南

适用场景

  1. 01构建 Drupal 自定义模块
  2. 02添加路由、表单或插件
  3. 03实现实体或数据库操作
  4. 04排查模块与 API 问题

安装

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

$npx skills add https://github.com/omedia/drupal-skill --skill drupal-backend

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

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/module-template/
MODULENAME.info.yml
MODULENAME.module
MODULENAME.routing.yml
README.md
assets/module-template/src/Controller/
ExampleController.php
references/
hooks.md
module_structure.md

详细介绍

Drupal 后端开发

概述

提供专家级 Drupal 后端开发能力。为 Drupal 8、9、10 和 11+ 的自定义模块开发、PHP 编程、API 使用、钩子、插件、服务和数据库操作提供全面指导。

何时使用此技能

在以下情况下使用此技能:

  • 自定义模块开发:创建新的 Drupal 模块
  • 钩子:实现 Drupal 钩子以更改系统行为
  • 控制器和路由:构建自定义页面和路由
  • 表单:创建配置表单或自定义表单
  • 实体:处理内容实体或配置实体
  • 插件:构建区块、字段类型、格式化程序或其他插件
  • 服务:创建可复用的业务逻辑服务
  • 数据库操作:自定义查询和架构定义
  • API:Entity API、Form API、Database API、Plugin API

核心能力

1. 自定义模块开发

创建完整且符合规范的 Drupal 模块:

快速入门工作流:

  1. 使用 assets/module-template/ 中的模块模板
  2. 将 MODULENAME 替换为机器名(小写,使用下划线)
  3. 将 MODULELABEL 替换为易读的名称
  4. 使用 Drupal API 实现功能
  5. 启用模块并测试:ddev drush en mymodule -y

模块结构:

  • .info.yml - 模块元数据和依赖项
  • .module - 钩子实现
  • .routing.yml - 路由定义
  • .services.yml - 服务定义
  • .permissions.yml - 自定义权限
  • src/ - PHP 类(PSR-4 自动加载)
  • config/ - 配置文件

参考文档:

  • references/module_structure.md - 完整的模块模式
  • references/hooks.md - 钩子实现
2. 钩子系统

实现钩子以更改 Drupal 行为:

常用钩子:

  • hook_entity_presave() - 保存前修改实体
  • hook_entity_insert/update/delete() - 响应实体更改
  • hook_form_alter() - 修改任意表单
  • hook_node_access() - 控制节点访问
  • hook_cron() - 执行定期任务
  • hook_install/uninstall() - 模块安装任务

钩子模式:

function MODULENAME_hook_name($param1, &$param2) {
  // Implementation
}

最佳实践:

  • 仅在 .module 文件中实现钩子
  • 使用恰当的类型提示
  • 使用 PHPDoc 注释块编写文档
  • 处理前检查实体类型
  • 留意性能影响
3. 控制器和路由

创建自定义页面和端点:

路由定义(.routing.yml):

mymodule.example:
  path: '/example/{param}'
  defaults:
    _controller: '\Drupal\mymodule\Controller\ExampleController::content'
    _title: 'Example Page'
  requirements:
    _permission: 'access content'
    param: \d+

控制器模式:

namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase;

class ExampleController extends ControllerBase {
  public function content() {
    return ['#markup' => $this->t('Hello!')];
  }
}

使用依赖注入:

public function __construct(EntityTypeManagerInterface $entity_type_manager) {
  $this->entityTypeManager = $entity_type_manager;
}

public static function create(ContainerInterface $container) {
  return new static($container->get('entity_type.manager'));
}
4. Forms API

构建配置表单和自定义表单:

配置表单:

class SettingsForm extends ConfigFormBase {
  protected function getEditableConfigNames() {
    return ['mymodule.settings'];
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('mymodule.settings');
    
    $form['api_key'] = [
      '#type' => 'textfield',
      '#title' => $this->t('API Key'),
      '#default_value' => $config->get('api_key'),
    ];
    
    return parent::buildForm($form, $form_state);
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->config('mymodule.settings')
      ->set('api_key', $form_state->getValue('api_key'))
      ->save();
    parent::submitForm($form, $form_state);
  }
}

表单验证:

public function validateForm(array &$form, FormStateInterface $form_state) {
  if (strlen($form_state->getValue('field')) < 5) {
    $form_state->setErrorByName('field', $this->t('Too short.'));
  }
}
5. Entity API

处理内容实体和配置实体:

加载实体:

// Load single entity
$node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);

// Load multiple entities
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple([1, 2, 3]);

// Load by properties
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadByProperties([
  'type' => 'article',
  'status' => 1,
]);

创建/保存实体:

$node = \Drupal::entityTypeManager()->getStorage('node')->create([
  'type' => 'article',
  'title' => 'My Article',
  'body' => ['value' => 'Content', 'format' => 'basic_html'],
]);
$node->save();

实体查询:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'article')
  ->condition('status', 1)
  ->accessCheck(TRUE)
  ->sort('created', 'DESC')
  ->range(0, 10);
$nids = $query->execute();
6. 插件系统

创建自定义插件(区块、字段等):

区块插件:

/**
 * @Block(
 *   id = "mymodule_custom_block",
 *   admin_label = @Translation("Custom Block"),
 * )
 */
class CustomBlock extends BlockBase {
  public function build() {
    return ['#markup' => 'Block content'];
  }
  
  public function blockForm($form, FormStateInterface $form_state) {
    $form['setting'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Setting'),
      '#default_value' => $this->configuration['setting'] ?? '',
    ];
    return $form;
  }
  
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['setting'] = $form_state->getValue('setting');
  }
}
7. 服务和依赖注入

创建可复用的服务:

服务定义(.services.yml):

services:
  mymodule.custom_service:
    class: Drupal\mymodule\Service\CustomService
    arguments: ['@entity_type.manager', '@current_user']

服务类:

namespace Drupal\mymodule\Service;

class CustomService {
  protected $entityTypeManager;
  protected $currentUser;

  public function __construct(EntityTypeManagerInterface $entity_type_manager, AccountProxyInterface $current_user) {
    $this->entityTypeManager = $entity_type_manager;
    $this->currentUser = $current_user;
  }

  public function doSomething() {
    // Business logic
  }
}
8. 数据库操作

执行自定义查询:

使用 Database API:

$database = \Drupal::database();

// Select query
$query = $database->select('node_field_data', 'n')
  ->fields('n', ['nid', 'title'])
  ->condition('type', 'article')
  ->condition('status', 1)
  ->range(0, 10);
$results = $query->execute()->fetchAll();

// Insert
$database->insert('mymodule_table')
  ->fields(['name' => 'Example', 'value' => 123])
  ->execute();

// Update
$database->update('mymodule_table')
  ->fields(['value' => 456])
  ->condition('name', 'Example')
  ->execute();

架构定义(.install 文件):

function mymodule_schema() {
  $schema['mymodule_table'] = [
    'fields' => [
      'id' => ['type' => 'serial', 'not null' => TRUE],
      'name' => ['type' => 'varchar', 'length' => 255, 'not null' => TRUE],
      'value' => ['type' => 'int', 'not null' => TRUE, 'default' => 0],
    ],
    'primary key' => ['id'],
    'indexes' => ['name' => ['name']],
  ];
  return $schema;
}

开发工作流

创建自定义模块
  1. 搭建模块框架:

    cp -r assets/module-template/ /path/to/drupal/modules/custom/mymodule/
    cd /path/to/drupal/modules/custom/mymodule/
    mv MODULENAME.info.yml mymodule.info.yml
    mv MODULENAME.module mymodule.module
    mv MODULENAME.routing.yml mymodule.routing.yml
    
  2. 更新模块文件:

    • 将 MODULENAME 替换为机器名
    • 将 MODULELABEL 替换为易读的名称
    • 更新 .info.yml 中的依赖项
    • 自定义控制器、路由和逻辑
  3. 启用并测试:

    ddev drush en mymodule -y
    ddev drush cr
    
  4. 迭代开发:

    • 实现功能
    • 清除缓存:ddev drush cr
    • 进行全面测试
    • 检查日志:ddev drush watchdog:tail
标准开发工作流
  1. 规划:确定所需的 API/钩子
  2. 编码:使用 Drupal API 实现
  3. 测试:启用模块、清除缓存并测试功能
  4. 调试:检查日志,必要时使用 Xdebug
  5. 优化:进行优化、添加测试并提升代码质量

最佳实践

模块开发
  1. PSR-4 自动加载:遵循命名空间约定
  2. 依赖注入:在类中使用 DI
  3. 钩子:仅在 .module 文件中实现
  4. 类型提示:使用正确的 PHP 类型声明
  5. 文档:添加 PHPDoc 注释块
  6. 测试:编写自动化测试
代码质量
  1. Drupal 编码标准:遵循 PHPCS 规则
  2. 安全性:验证输入、清理输出、检查权限
  3. 性能:尽可能使用缓存并优化查询
  4. 无障碍性:确保表单和页面可访问
  5. 国际化:对所有字符串使用 $this->t()
API 使用
  1. 实体 API:优先使用实体操作,而非直接查询数据库
  2. 配置:使用 Config API 管理设置
  3. 状态 API:用于临时或不可导出的数据
  4. 缓存 API:为开销较大的操作实现缓存
  5. 队列 API:用于长时间运行的任务

常见模式

事件订阅器
class MyModuleSubscriber implements EventSubscriberInterface {
  public static function getSubscribedEvents() {
    return [KernelEvents::REQUEST => ['onRequest', 0]];
  }

  public function onRequest(RequestEvent $event) {
    // Handle event
  }
}
自定义权限
# mymodule.permissions.yml
administer mymodule:
  title: 'Administer My Module'
  restrict access: true
自定义访问检查
class CustomAccessCheck implements AccessInterface {
  public function access(AccountInterface $account) {
    return AccessResult::allowedIf($account->hasPermission('access content'));
  }
}

故障排除

模块未显示
  • 检查 .info.yml 语法
  • 验证 core_version_requirement
  • 清除缓存:ddev drush cr
钩子无法正常工作
  • 确认钩子名称正确
  • 添加钩子后清除缓存
  • 如果钩子顺序很重要,请检查模块权重
找不到类
  • 验证 PSR-4 命名空间与目录匹配
  • 清除缓存以重建类注册表
  • 使用 composer dump-autoload 检查自动加载
数据库错误
  • 检查架构定义语法
  • 架构变更后运行 ddev drush updb
  • 验证查询中的表名和列名

资源

参考文档
  • references/hooks.md - 常见钩子及示例

    • 实体钩子
    • 表单钩子
    • 节点钩子
    • 安装钩子
    • 令牌钩子
  • references/module_structure.md - 模块模式

    • 控制器和路由
    • 表单(配置和自定义)
    • 插件(区块、字段等)
    • 服务和 DI
    • 事件订阅器
资源模板
  • assets/module-template/ - 模块脚手架
    • .info.yml 元数据
    • .module 钩子文件
    • .routing.yml 路由
    • 示例控制器

版本兼容性

Drupal 8 与 9、10、11
  • 核心 API:各版本之间基本一致
  • PHP 要求:8.x(7.0+)、9.x(7.3+)、10.x(8.1+)、11.x(8.3+)
  • 弃用项:升级时请查看变更记录
  • Symfony:不同 Drupal 版本使用不同的 Symfony 版本

另请参阅

  • drupal-frontend - 主题开发、Twig 模板
  • drupal-tooling - DDEV 和 Drush 开发工具
  • Drupal API - 官方 API 文档

评论

支持 Markdown