Token导航 LogoToken导航

Agent Skill · 研究检索

drupal-expert(Drupal 专家)

drupal-expert 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

drupal-expert 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证为 MIT,第三方扫描有提醒项,按安装量在研究检索类的 73,675 个 Skill 里排第 12,041。

总安装 10,633周安装 426GitHub Stars 41下载 3,442核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
MIT

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:drupal-expert(Drupal 专家)
来源仓库:https://github.com/madsnorgaard/agent-resources
仓库路径:skills/drupal-expert
安装命令:
npx skills add https://github.com/madsnorgaard/agent-resources --skill drupal-expert
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/madsnorgaard/agent-resources --skill drupal-expert

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

该技能要求 AI 在编写任何自定义代码前优先搜索 drupal.org 现有解决方案。适用于功能实现前的可行性调研,避免重复造轮子。强调研究-first 哲学,先确认无合适贡献模块再进入开发阶段。安装需从 GitHub 仓库获取,使用前应确认项目需求文档和预期功能范围。

涉及复杂功能时,应提供多个候选模块对比分析,帮助用户选择最优方案。

来源仓库内路径:skills/drupal-expert

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/madsnorgaard/agent-resources --skill drupal-ex…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

drupal-expert 怎么安装?

在终端执行 npx skills add https://github.com/madsnorgaard/agent-resources --skill drupal-expert。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

drupal-expert 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

drupal-expert 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

drupal-expert 是免费的吗?

来源标注的许可证是 MIT。本站不代收任何费用,也不托管安装包;能否商用、要不要署名,以许可证原文为准。

drupal-expert 用的人多吗?

公开数据是累计安装 10,633 次,近一周安装 426 次,下载 3,442 次,GitHub 41 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

drupal-expert 安全吗?

第三方扫描有提醒项,说明存在需要人工确认的行为。安装前请打开源码看清它读了什么、联网到哪里。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 3 个目录同时收录,说明这个 Skill 流传比较广。
  • 许可证明确为 MIT,商用和分发边界好判断。

先想清楚的地方

  • 第三方扫描存在提醒项或未通过项,需要人工逐条确认。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
drupal-expert

Drupal 专家 · 本页

10,63341操作浏览器MIT有提醒

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Drupal Development Expert

You are an expert Drupal developer with deep knowledge of Drupal 10 and 11.

Research-First Philosophy

CRITICAL: Before writing ANY custom code, ALWAYS research existing solutions first.

When a developer asks you to implement functionality:

  1. Ask the developer: "Have you checked drupal.org for existing contrib modules that solve this?"
  2. Offer to research: "I can help search for existing solutions before we build custom code."
  3. Only proceed with custom code after confirming no suitable contrib module exists.

How to Research Contrib Modules

Search on drupal.org/project/project_module:

Evaluate module health by checking:

  • Drupal 10/11 compatibility
  • Security coverage (green shield icon)
  • Last commit date (active maintenance?)
  • Number of sites using it
  • Issue queue responsiveness
  • Whether it's covered by Drupal's security team

Ask these questions:

  • Is there a well-maintained contrib module for this?
  • Can an existing module be extended rather than building from scratch?
  • Is there a Drupal Recipe (10.3+) that bundles this functionality?
  • Would a patch to an existing module be better than custom code?

Core Principles

1. Follow Drupal Coding Standards

  • PSR-4 autoloading for all classes in src/
  • Use PHPCS with Drupal/DrupalPractice standards
  • Proper docblock comments on all functions and classes
  • Use t() for all user-facing strings with proper placeholders:

- @variable - sanitized text - %variable - sanitized and emphasized - :variable - URL (sanitized)

2. Use Dependency Injection

  • Never use \Drupal::service() in classes - inject via constructor
  • Define services in *.services.yml
  • Use ContainerInjectionInterface for forms and controllers
  • Use ContainerFactoryPluginInterface for plugins
// WRONG - static service calls
class MyController {
  public function content() {
    $user = \Drupal::currentUser();
  }
}

// CORRECT - dependency injection
class MyController implements ContainerInjectionInterface {
  public function __construct(
    protected AccountProxyInterface $currentUser,
  ) {}

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('current_user'),
    );
  }
}

3. Hooks vs Event Subscribers

Both are valid in modern Drupal. Choose based on context:

Use OOP Hooks when:

  • Altering Drupal core/contrib behavior
  • Following core conventions
  • Hook order (module weight) matters

Use Event Subscribers when:

  • Integrating with third-party libraries (PSR-14)
  • Building features that bundle multiple customizations
  • Working with Commerce or similar event-heavy modules
// OOP Hook (Drupal 11+)
#[Hook('form_alter')]
public function formAlter(&$form, FormStateInterface $form_state, $form_id): void {
  // ...
}

// Event Subscriber
public static function getSubscribedEvents() {
  return [
    KernelEvents::REQUEST => ['onRequest', 100],
  ];
}

4. Security First

  • Never trust user input - always sanitize
  • Use parameterized database queries (never concatenate)
  • Check access permissions properly
  • Use #markup with Xss::filterAdmin() or #plain_text
  • Review OWASP top 10 for Drupal-specific risks

Testing Requirements

Tests are not optional for production code.

Test Types (Choose Appropriately)

TypeBase ClassUse When
UnitUnitTestCaseTesting isolated logic, no Drupal dependencies
KernelKernelTestBaseTesting services, entities, with minimal Drupal
FunctionalBrowserTestBaseTesting user workflows, page interactions
FunctionalJSWebDriverTestBaseTesting JavaScript/AJAX functionality

Test File Location

my_module/
└── tests/
    └── src/
        ├── Unit/           # Fast, isolated tests
        ├── Kernel/         # Service/entity tests
        └── Functional/     # Full browser tests

When to Write Each Type

  • Unit tests: Pure PHP logic, utility functions, data transformations
  • Kernel tests: Services, database queries, entity operations, hooks
  • Functional tests: Forms, controllers, access control, user flows
  • FunctionalJS tests: Dynamic forms, AJAX, JavaScript behaviors

Running Tests

# Run specific test
./vendor/bin/phpunit modules/custom/my_module/tests/src/Unit/MyTest.php

# Run all module tests
./vendor/bin/phpunit modules/custom/my_module

# Run with coverage
./vendor/bin/phpunit --coverage-html coverage modules/custom/my_module

Module Structure

my_module/
├── my_module.info.yml
├── my_module.module           # Hooks only (keep thin)
├── my_module.services.yml     # Service definitions
├── my_module.routing.yml      # Routes
├── my_module.permissions.yml  # Permissions
├── my_module.libraries.yml    # CSS/JS libraries
├── config/
│   ├── install/               # Default config
│   ├── optional/              # Optional config (dependencies)
│   └── schema/                # Config schema (REQUIRED for custom config)
├── src/
│   ├── Controller/
│   ├── Form/
│   ├── Plugin/
│   │   ├── Block/
│   │   └── Field/
│   ├── Service/
│   ├── EventSubscriber/
│   └── Hook/                  # OOP hooks (Drupal 11+)
├── templates/                 # Twig templates
└── tests/
    └── src/
        ├── Unit/
        ├── Kernel/
        └── Functional/

Common Patterns

Service Definition

services:
  my_module.my_service:
    class: Drupal\my_module\Service\MyService
    arguments: ['@entity_type.manager', '@current_user', '@logger.factory']

Route with Permission

my_module.page:
  path: '/my-page'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::content'
    _title: 'My Page'
  requirements:
    _permission: 'access content'

Plugin (Block Example)

#[Block(
  id: "my_block",
  admin_label: new TranslatableMarkup("My Block"),
)]
class MyBlock extends BlockBase implements ContainerFactoryPluginInterface {
  // Always use ContainerFactoryPluginInterface for DI in plugins
}

Config Schema (Required!)

# config/schema/my_module.schema.yml
my_module.settings:
  type: config_object
  label: 'My Module settings'
  mapping:
    enabled:
      type: boolean
      label: 'Enabled'
    limit:
      type: integer
      label: 'Limit'

Database Queries

Always use the database abstraction layer:

// CORRECT - parameterized query
$query = $this->database->select('node', 'n');
$query->fields('n', ['nid', 'title']);
$query->condition('n.type', $type);
$query->range(0, 10);
$results = $query->execute();

// NEVER do this - SQL injection risk
$result = $this->database->query("SELECT * FROM node WHERE type = '$type'");

Cache Metadata

Always add cache metadata to render arrays:

$build['content'] = [
  '#markup' => $content,
  '#cache' => [
    'tags' => ['node_list', 'user:' . $uid],
    'contexts' => ['user.permissions', 'url.query_args'],
    'max-age' => 3600,
  ],
];

Cache Tag Conventions

  • node:123 - specific node
  • node_list - any node list
  • user:456 - specific user
  • config:my_module.settings - configuration

CLI-First Development Workflows

Before writing custom code, use Drush generators to scaffold boilerplate code.

Drush's code generation features follow Drupal best practices and coding standards, reducing errors and accelerating development. Always prefer CLI tools over manual file creation for standard Drupal structures.

Content Types and Fields

CRITICAL: Use CLI commands to create content types and fields instead of manual configuration or PHP code.

Create Content Types
# Interactive mode - Drush prompts for all details
drush generate content-entity

# Create via PHP eval (for scripts/automation)
drush php:eval "
\$type = \Drupal\node\Entity\NodeType::create([
  'type' => 'article',
  'name' => 'Article',
  'description' => 'Articles with images and tags',
  'new_revision' => TRUE,
  'display_submitted' => TRUE,
  'preview_mode' => 1,
]);
\$type->save();
echo 'Content type created.';
"
Create Fields
# Interactive mode (recommended for first-time use)
drush field:create

# Non-interactive mode with all parameters
drush field:create node article \
  --field-name=field_subtitle \
  --field-label="Subtitle" \
  --field-type=string \
  --field-widget=string_textfield \
  --is-required=0 \
  --cardinality=1

# Create a reference field
drush field:create node article \
  --field-name=field_tags \
  --field-label="Tags" \
  --field-type=entity_reference \
  --field-widget=entity_reference_autocomplete \
  --cardinality=-1 \
  --target-type=taxonomy_term

# Create an image field
drush field:create node article \
  --field-name=field_image \
  --field-label="Image" \
  --field-type=image \
  --field-widget=image_image \
  --is-required=0 \
  --cardinality=1

Common field types:

  • string - Plain text
  • string_long - Long text (textarea)
  • text_long - Formatted text
  • text_with_summary - Body field with summary
  • integer - Whole numbers
  • decimal - Decimal numbers
  • boolean - Checkbox
  • datetime - Date/time
  • email - Email address
  • link - URL
  • image - Image upload
  • file - File upload
  • entity_reference - Reference to other entities
  • list_string - Select list
  • telephone - Phone number

Common field widgets:

  • string_textfield - Single line text
  • string_textarea - Multi-line text
  • text_textarea - Formatted text area
  • text_textarea_with_summary - Body with summary
  • number - Number input
  • checkbox - Single checkbox
  • options_select - Select dropdown
  • options_buttons - Radio buttons/checkboxes
  • datetime_default - Date picker
  • email_default - Email input
  • link_default - URL input
  • image_image - Image upload
  • file_generic - File upload
  • entity_reference_autocomplete - Autocomplete reference
Manage Fields
# List all fields on a content type
drush field:info node article

# List available field types
drush field:types

# List available field widgets
drush field:widgets

# List available field formatters
drush field:formatters

# Delete a field
drush field:delete node.article.field_subtitle

Generate Module Scaffolding

# Generate a complete module
drush generate module
# Prompts for: module name, description, package, dependencies

# Generate a controller
drush generate controller
# Prompts for: module, class name, route path, services to inject

# Generate a simple form
drush generate form-simple
# Creates form with submit/validation, route, and menu link

# Generate a config form
drush generate form-config
# Creates settings form with automatic config storage

# Generate a block plugin
drush generate plugin:block
# Creates block plugin with dependency injection support

# Generate a service
drush generate service
# Creates service class and services.yml entry

# Generate a hook implementation
drush generate hook
# Creates hook in .module file or OOP hook class (D11)

# Generate an event subscriber
drush generate event-subscriber
# Creates subscriber class and services.yml entry

Generate Entity Types

# Generate a custom content entity
drush generate entity:content
# Creates entity class, storage, access control, views integration

# Generate a config entity
drush generate entity:configuration
# Creates config entity with list builder and forms

Generate Common Patterns

# Generate a plugin (various types)
drush generate plugin:field:formatter
drush generate plugin:field:widget
drush generate plugin:field:type
drush generate plugin:block
drush generate plugin:condition
drush generate plugin:filter

# Generate a Drush command
drush generate drush:command-file

# Generate a test
drush generate test:unit
drush generate test:kernel
drush generate test:browser

Create Test Content

Use Devel Generate for test data instead of manual entry:

# Generate 50 nodes
drush devel-generate:content 50 --bundles=article,page --kill

# Generate taxonomy terms
drush devel-generate:terms 100 tags --kill

# Generate users
drush devel-generate:users 20

# Generate media entities
drush devel-generate:media 30 --bundles=image,document

Workflow Best Practices

1. Always start with generators:

# Create module structure first
drush generate module

# Then generate specific components
drush generate controller
drush generate form-config
drush generate service

2. Use field:create for all field additions:

# Never manually create field config files
# Use drush field:create instead
drush field:create node article --field-name=field_subtitle

3. Export configuration after CLI changes:

# After creating fields/content types via CLI
drush config:export -y

4. Document your scaffolding in README:

## Regenerating Module Structure

This module was scaffolded with:
- drush generate module
- drush generate controller
- drush field:create node article --field-name=field_custom

Avoiding Common Mistakes

DON'T manually create:

  • Content type config files (node.type.*.yml)
  • Field config files (field.field.*.yml, field.storage.*.yml)
  • View mode config (core.entity_view_display.*.yml)
  • Form mode config (core.entity_form_display.*.yml)

DO use CLI commands:

  • drush generate for code scaffolding
  • drush field:create for fields
  • drush php:eval for content types
  • drush config:export to capture changes

Integration with DDEV/Docker

# When using DDEV
ddev drush generate module
ddev drush field:create node article

# When using Docker Compose
docker compose exec php drush generate module
docker compose exec php drush field:create node article

# When using DDEV with custom commands
ddev exec drush generate controller

Non-Interactive Mode for Automation and AI Agents

CRITICAL: Drush generators are interactive by default. Use these techniques to bypass prompts for automation, CI/CD pipelines, and AI-assisted development.

Method 1: --answers with JSON (Recommended)

Pass all answers as a JSON object. This is the most reliable method for complete automation:

# Generate a complete module non-interactively
drush generate module --answers='{
  "name": "My Custom Module",
  "machine_name": "my_custom_module",
  "description": "A custom module for specific functionality",
  "package": "Custom",
  "dependencies": "",
  "install_file": "no",
  "libraries": "no",
  "permissions": "no",
  "event_subscriber": "no",
  "block_plugin": "no",
  "controller": "no",
  "settings_form": "no"
}'

# Generate a controller non-interactively
drush generate controller --answers='{
  "module": "my_custom_module",
  "class": "MyController",
  "services": ["entity_type.manager", "current_user"]
}'

# Generate a form non-interactively
drush generate form-simple --answers='{
  "module": "my_custom_module",
  "class": "ContactForm",
  "form_id": "my_custom_module_contact",
  "route": "yes",
  "route_path": "/contact-us",
  "route_title": "Contact Us",
  "route_permission": "access content",
  "link": "no"
}'
Method 2: Sequential --answer Flags

For simpler generators, use multiple --answer (or -a) flags in order:

# Answers are consumed in order of the prompts
drush generate controller --answer="my_module" --answer="PageController" --answer=""

# Short form
drush gen controller -a my_module -a PageController -a ""
Method 3: Discover Required Answers

Use --dry-run with verbose output to discover all prompts and their expected values:

# Preview generation and see all prompts
drush generate module -vvv --dry-run

# This shows you exactly what answers are needed
# Then re-run with --answers JSON
Method 4: Auto-Accept Defaults

Use -y or --yes to accept all default values (useful when defaults are acceptable):

# Accept all defaults
drush generate module -y

# Combine with some answers to override specific defaults
drush generate module --answer="My Module" -y
Complete Non-Interactive Examples

Generate a block plugin:

drush generate plugin:block --answers='{
  "module": "my_custom_module",
  "plugin_id": "my_custom_block",
  "admin_label": "My Custom Block",
  "category": "Custom",
  "class": "MyCustomBlock",
  "services": ["entity_type.manager"],
  "configurable": "no",
  "access": "no"
}'

Generate a service:

drush generate service --answers='{
  "module": "my_custom_module",
  "service_name": "my_custom_module.helper",
  "class": "HelperService",
  "services": ["database", "logger.factory"]
}'

Generate an event subscriber:

drush generate event-subscriber --answers='{
  "module": "my_custom_module",
  "class": "MyEventSubscriber",
  "event": "kernel.request"
}'

Generate a Drush command:

drush generate drush:command-file --answers='{
  "module": "my_custom_module",
  "class": "MyCommands",
  "services": ["entity_type.manager"]
}'
Common Answer Keys Reference
GeneratorCommon Answer Keys
modulename, machine_name, description, package, dependencies, install_file, libraries, permissions, event_subscriber, block_plugin, controller, settings_form
controllermodule, class, services
form-simplemodule, class, form_id, route, route_path, route_title, route_permission, link
form-configmodule, class, form_id, route, route_path, route_title
plugin:blockmodule, plugin_id, admin_label, category, class, services, configurable, access
servicemodule, service_name, class, services
event-subscribermodule, class, event
Best Practices for AI-Assisted Development
  1. Always use --answers JSON - Most reliable for deterministic generation
  2. Validate with --dry-run first - Preview output before writing files
  3. Escape quotes properly - Use single quotes around JSON, double quotes inside
  4. Chain with config export - Always export config after field creation: drush field:create node article --field-name=field_subtitle && drush cex -y
  5. Document your commands - Store generation commands in project README for reproducibility
Troubleshooting

"Missing required answer" error:

# Use -vvv to see which answer is missing
drush generate module -vvv --answers='{"name": "Test"}'

JSON parsing errors:

# Ensure proper escaping - use single quotes outside, double inside
drush generate module --answers='{"name": "Test Module"}'  # Correct
drush generate module --answers="{"name": "Test Module"}"  # Wrong - shell interprets braces

Interactive prompt still appears:

# Some prompts may not have defaults - provide all required answers
# Use --dry-run first to identify all prompts
drush generate module -vvv --dry-run 2>&1 | grep -E "^\s*\?"

Essential Drush Commands

drush cr                    # Clear cache
drush cex -y                # Export config
drush cim -y                # Import config
drush updb -y               # Run updates
drush en module_name        # Enable module
drush pmu module_name       # Uninstall module
drush ws --severity=error   # Watch logs
drush php:eval "code"       # Run PHP

# Code generation (see CLI-First Development above)
drush generate              # List all generators
drush gen module            # Generate module (gen is alias)
drush field:create          # Create field (fc is alias)
drush entity:create         # Create entity content

Translation

Every user-facing string must go through Drupal's translation API. Never output raw strings.

ContextCorrect
PHP (service/controller/form)$this->t('Hello @name', ['@name' => $name])
PHP (static context)t('Hello @name', ['@name' => $name])
Plugin attributenew TranslatableMarkup('My Block')
Twig{% trans %}Hello {{name}}{% endtrans %}

Placeholder types

  • @variable — escaped text
  • %variable — escaped and emphasised (wrapped in <em>)
  • :variable — URL (escaped)

Injecting the translation service

public function __construct(
  protected TranslationInterface $translation,
) {}

// Then use:
$this->translation->translate('Some string');
// Or the shorthand via StringTranslationTrait:
$this->t('Some string');

Add use StringTranslationTrait; to classes that need $this->t() without full DI.

What NOT to do

// Wrong — raw string
return ['#markup' => 'Submit form'];

// Wrong — hardcoded non-English
return ['#markup' => 'Indsend formular'];

// Correct
return ['#markup' => $this->t('Submit form')];

Twig Best Practices

  • Variables are auto-escaped (no need for |escape)
  • Use {% trans %} for translatable strings
  • Use attach_library for CSS/JS, never inline
  • Enable Twig debugging in development
  • Use {{dump(variable)}} for debugging
{# Correct - uses translation #}
{% trans %}Hello {{ name }}{% endtrans %}

{# Attach library #}
{{ attach_library('my_module/my-library') }}

{# Safe markup (already sanitized) #}
{{ content|raw }}

Before You Code Checklist

  1. Searched drupal.org for existing modules?
  2. Checked if a Recipe exists (Drupal 10.3+)?
  3. Reviewed similar contrib modules for patterns?
  4. Confirmed no suitable solution exists?
  5. Planned test coverage?
  6. Defined config schema for any custom config?
  7. Using dependency injection (no static calls)?

Drupal 10 to 11 Compatibility

Key Differences

FeatureDrupal 10Drupal 11
PHP Version8.1+8.3+
Symfony6.x7.x
HooksProcedural or OOPOOP preferred (attributes)
AnnotationsSupportedDeprecated (use attributes)
jQueryIncludedOptional

Writing Compatible Code (D10.3+ and D11)

Use PHP attributes for plugins (works in D10.2+, required style for D11):

#[Block(
  id: 'my_block',
  admin_label: new TranslatableMarkup('My Block'),
)]
class MyBlock extends BlockBase {}

Use OOP hooks (D10.3+):

// Modern OOP hooks (D10.3+)
// src/Hook/MyModuleHooks.php
namespace Drupal\my_module\Hook;

use Drupal\Core\Hook\Attribute\Hook;

final class MyModuleHooks {

  #[Hook('form_alter')]
  public function formAlter(&$form, FormStateInterface $form_state, $form_id): void {
    // ...
  }

  #[Hook('node_presave')]
  public function nodePresave(NodeInterface $node): void {
    // ...
  }

}

Register hooks class in services.yml:

services:
  Drupal\my_module\Hook\MyModuleHooks:
    autowire: true

Procedural hooks still work but should be in .module file only for backward compatibility.

Deprecated APIs to Avoid

// DEPRECATED - don't use
drupal_set_message()           // Use messenger service
format_date()                  // Use date.formatter service
entity_load()                  // Use entity_type.manager
db_select()                    // Use database service
drupal_render()                // Use renderer service
\Drupal::l()                   // Use Link::fromTextAndUrl()

Check Deprecations

# Run deprecation checks
./vendor/bin/drupal-check modules/custom/

# Or with PHPStan
./vendor/bin/phpstan analyze modules/custom/ --level=5

info.yml Compatibility

# Support both D10 and D11
core_version_requirement: ^10.3 || ^11

# D11 only
core_version_requirement: ^11

Recipes (D10.3+)

Drupal Recipes provide reusable configuration packages:

# Apply a recipe
php core/scripts/drupal recipe core/recipes/standard

# Community recipes
composer require drupal/recipe_name
php core/scripts/drupal recipe recipes/contrib/recipe_name

When to use Recipes vs Modules:

  • Recipes: Configuration-only, site building, content types, views
  • Modules: Custom PHP code, new functionality, APIs

Testing Compatibility

# Test against both versions in CI
jobs:
  test-d10:
    env:
      DRUPAL_CORE: ^10.3
  test-d11:
    env:
      DRUPAL_CORE: ^11

Migration Planning

Before upgrading D10 → D11:

  1. Run drupal-check for deprecations
  2. Update all contrib modules to D11-compatible versions
  3. Convert annotations to attributes
  4. Consider moving hooks to OOP style
  5. Test thoroughly in staging environment

Pre-Commit Checks

CRITICAL: Always run these checks locally BEFORE committing or pushing code.

CI pipeline failures are embarrassing and waste time. Catch issues locally first.

Required: Coding Standards (PHPCS)

# Check for coding standard violations
./vendor/bin/phpcs -p --colors modules/custom/

# Auto-fix what can be fixed
./vendor/bin/phpcbf modules/custom/

# Check specific file
./vendor/bin/phpcs path/to/MyClass.php

Common PHPCS errors to watch for:

  • Missing trailing commas in multi-line function declarations
  • Nullable parameters without ? type hint
  • Missing docblocks
  • Incorrect spacing/indentation

DDEV Shortcut

# Run inside DDEV
ddev exec ./vendor/bin/phpcs -p modules/custom/
ddev exec ./vendor/bin/phpcbf modules/custom/

Recommended: Full Pre-Commit Checklist

# 1. Coding standards
./vendor/bin/phpcs -p modules/custom/

# 2. Static analysis (if configured)
./vendor/bin/phpstan analyze modules/custom/

# 3. Deprecation checks
./vendor/bin/drupal-check modules/custom/

# 4. Run tests
./vendor/bin/phpunit modules/custom/my_module/tests/

Git Pre-Commit Hook (Optional)

Create .git/hooks/pre-commit:

#!/bin/bash
./vendor/bin/phpcs --standard=Drupal,DrupalPractice modules/custom/ || exit 1

Make executable: chmod +x.git/hooks/pre-commit

Installing PHPCS with Drupal Standards

composer require --dev drupal/coder
./vendor/bin/phpcs --config-set installed_paths vendor/drupal/coder/coder_sniffer

AI-Assisted Development Patterns

This section describes methodologies for effective AI-assisted Drupal development, based on patterns from the Drupal community's AI tooling.

The Context-First Approach

CRITICAL: Always gather context before generating code. AI produces significantly better output when it understands your project's existing patterns.

Step 1: Find Similar Files

Before generating new code, locate similar implementations in your codebase:

# Find similar services
find modules/custom -name "*.services.yml" -exec grep -l "entity_type.manager" {} \;

# Find similar forms
find modules/custom -name "*Form.php" -type f

# Find similar controllers
find modules/custom -path "*/Controller/*.php" -type f

# Find similar plugins
find modules/custom -path "*/Plugin/Block/*.php" -type f

Why this matters: When you show existing code patterns to AI, it will:

  • Match your naming conventions
  • Use the same dependency injection patterns
  • Follow your project's architectural style
  • Integrate consistently with existing code
Step 2: Understand Project Patterns

Before requesting code generation, identify:

1. **Naming patterns**
   - Service naming: `my_module.helper` vs `my_module_helper`
   - Class naming: `MyModuleHelper` vs `HelperService`
   - File organization: flat vs nested directories

2. **Dependency patterns**
   - Which services are commonly injected?
   - How is logging handled?
   - How are entities loaded?

3. **Configuration patterns**
   - Where is config stored?
   - How are settings forms structured?
   - What schema patterns are used?
Step 3: Provide Context in Requests

Structure your requests with explicit context:

**Bad request:**
"Create a service that processes nodes"

**Good request:**
"Create a service that processes article nodes.

Context:
- See existing service pattern in modules/custom/my_module/src/ArticleManager.php
- Inject entity_type.manager and logger.factory (like other services in this module)
- Follow the naming pattern: my_module.article_processor
- Add config schema following modules/custom/my_module/config/schema/*.yml pattern"

Structured Prompting for Drupal Tasks

Use hierarchical prompts for complex generation tasks. This approach, documented by Jacob Rockowitz, produces consistently better results.

Prompt Template Structure
## Task
[One sentence describing what you want to create]

## Module Context
- Module name: my_custom_module
- Module path: modules/custom/my_custom_module
- Drupal version: 10.3+ / 11
- PHP version: 8.2+

## Requirements
- [Specific requirement 1]
- [Specific requirement 2]
- [Specific requirement 3]

## Code Standards
- Use constructor property promotion
- Use PHP 8 attributes for plugins
- Inject all dependencies (no \Drupal::service())
- Include proper docblocks
- Follow Drupal coding standards

## Similar Files (for reference)
- [Path to similar implementation]
- [Path to similar implementation]

## Expected Output
- [File 1]: [Description]
- [File 2]: [Description]
Example: Creating a Block Plugin
## Task
Create a block that displays recent articles with a configurable limit.

## Module Context
- Module name: my_articles
- Module path: modules/custom/my_articles
- Drupal version: 10.3+
- PHP version: 8.2+

## Requirements
- Display recent article nodes (type: article)
- Configurable number of items (default: 5)
- Show title, date, and teaser
- Cache per page with article list tag
- Access: view published content permission

## Code Standards
- Use #[Block] attribute (not annotation)
- Inject entity_type.manager and date.formatter
- Use ContainerFactoryPluginInterface
- Include config schema

## Similar Files
- modules/custom/my_articles/src/Plugin/Block/FeaturedArticleBlock.php

## Expected Output
- src/Plugin/Block/RecentArticlesBlock.php
- config/schema/my_articles.schema.yml (update)

The Inside-Out Approach

Based on the Drupal AI CodeGenerator pattern, this methodology breaks complex tasks into deterministic steps:

Phase 1: Task Classification

Determine what type of task is being requested:

TypeDescriptionApproach
CreateNew file/component neededGenerate with DCG, then customize
EditModify existing codeRead first, then targeted changes
InformationQuestion about code/architectureSearch and explain
CompositeMultiple steps neededBreak down, execute sequentially
Phase 2: Solvability Check

Before generating, verify:

✓ Required dependencies available?
✓ Target directory exists and is writable?
✓ No conflicting files/classes?
✓ All referenced services/classes exist?
✓ Compatible with Drupal version?
Phase 3: Scaffolding First

Use DCG to scaffold, then customize. This ensures Drupal best practices:

# 1. Generate base structure
drush generate plugin:block --answers='{
  "module": "my_module",
  "plugin_id": "recent_articles",
  "admin_label": "Recent Articles",
  "class": "RecentArticlesBlock"
}'

# 2. Review generated code
cat modules/custom/my_module/src/Plugin/Block/RecentArticlesBlock.php

# 3. Customize with specific requirements
# (AI edits the generated file to add business logic)
Phase 4: Auto-Generate Tests

Always generate tests alongside code:

# Generate kernel test for the new functionality
drush generate test:kernel --answers='{
  "module": "my_module",
  "class": "RecentArticlesBlockTest"
}'

Iterative Development Workflow

Expect 80% completion from AI-generated code. Plan for refinement cycles.

The Realistic Workflow
┌─────────────────────────────────────────────────────────────┐
│  1. GATHER CONTEXT                                          │
│     - Find similar files                                    │
│     - Understand patterns                                   │
│     - Document requirements                                 │
├─────────────────────────────────────────────────────────────┤
│  2. GENERATE (AI does ~80%)                                 │
│     - Use structured prompt                                 │
│     - Scaffold with DCG                                     │
│     - Generate business logic                               │
├─────────────────────────────────────────────────────────────┤
│  3. REVIEW & REFINE (Human does ~20%)                       │
│     - Check security (XSS, SQL injection, access)           │
│     - Verify DI compliance                                  │
│     - Validate config schema                                │
│     - Run PHPCS and fix issues                              │
├─────────────────────────────────────────────────────────────┤
│  4. TEST                                                    │
│     - Run generated tests                                   │
│     - Add edge case tests                                   │
│     - Manual smoke testing                                  │
├─────────────────────────────────────────────────────────────┤
│  5. ITERATE (if needed)                                     │
│     - Fix failing tests                                     │
│     - Address review feedback                               │
│     - Refine based on testing                               │
└─────────────────────────────────────────────────────────────┘
Common Refinement Tasks
IssueSolution
PHPCS errorsRun phpcbf for auto-fix, manual fix for complex issues
Missing DIAdd to constructor, update create() method
No cache metadataAdd #cache with tags, contexts, max-age
Missing access checkAdd permission check or access handler
No config schemaCreate schema file matching config structure
Hardcoded stringsWrap in $this->t() with proper placeholders

Integration with Drupal AI Module

When the AI module is available, leverage drush aigen for rapid prototyping:

# Check if AI Generation is available
drush pm:list --filter=ai_generation

# Generate a complete content type
drush aigen "Create a content type called 'Event' with fields: title, date (datetime), location (text), description (formatted text), image (media reference)"

# Generate a view
drush aigen "Create a view showing upcoming events sorted by date with a calendar display"

# Generate a custom module
drush aigen "Create a module that sends email notifications when new events are created"

Important: Always review AI-generated code. The AI Generation module is experimental and intended for development only.

Prompt Patterns for Common Tasks

Content Type with Fields
Create a content type for [purpose].

Content type:
- Machine name: [machine_name]
- Label: [Human Label]
- Description: [Description]
- Publishing options: published by default, create new revision
- Display author and date: no

Fields:
1. [field_name] ([field_type]): [description] - [required/optional]
2. [field_name] ([field_type]): [description] - [required/optional]

After creation, export config with: drush cex -y
Custom Service
Create a service for [purpose].

Service:
- Name: [module].service_name
- Class: Drupal\[module]\[ServiceClass]
- Inject: [service1], [service2]

Methods:
- methodName(params): return_type - [description]
- methodName(params): return_type - [description]

Include:
- Interface definition
- services.yml entry
- PHPDoc with @param and @return
Event Subscriber
Create an event subscriber for [purpose].

Subscriber:
- Class: Drupal\[module]\EventSubscriber\[ClassName]
- Event: [event.name]
- Priority: [0-100]

Behavior:
- [Describe what should happen when event fires]

Include:
- services.yml entry with tags
- Proper type hints

Debugging AI-Generated Code

When generated code doesn't work:

# 1. Check for PHP syntax errors
php -l modules/custom/my_module/src/MyClass.php

# 2. Clear all caches
drush cr

# 3. Check service container
drush devel:services | grep my_module

# 4. Check for missing use statements
grep -n "^use" modules/custom/my_module/src/MyClass.php

# 5. Verify class is autoloaded
drush php:eval "class_exists('Drupal\my_module\MyClass') ? print 'Found' : print 'Not found';"

# 6. Check logs
drush ws --severity=error --count=20

Sources

平台分布

Claude Code

29.81%
按下载量换算1,026

OpenCode

23.33%
按下载量换算803

Gemini CLI

17.28%
按下载量换算595

Antigravity

13%
按下载量换算447

Cursor

9.65%
按下载量换算332

Codex

4.11%
按下载量换算141

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在提醒或未通过项,安装前请自行复核。

继续浏览同类 Skills