drupal-backend について
drupal-backendは、Drupal 8から11+までのバックエンド開発向けスキルです。カスタムモジュール、Drupal API、フック、プラグイン、サービス、ルーティング、フォーム、データベース操作のパターンと例を提供します。
主な機能
- 01カスタムモジュールの雛形と構成
- 02フック、コントローラー、ルーティング
- 03フォームとエンティティ操作
- 04プラグイン、サービス、依存性注入
- 05データベースクエリとスキーマ定義
- 06開発とトラブルシューティングの指針
活用シーン
- 01Drupalカスタムモジュールの構築
- 02ルート、フォーム、プラグインの追加
- 03エンティティまたはデータベース操作の実装
- 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.ymlMODULENAME.moduleMODULENAME.routing.ymlREADME.mdassets/module-template/src/Controller/ExampleController.phpreferences/hooks.mdmodule_structure.md詳細
Drupal バックエンド開発
概要
エキスパートレベルの Drupal バックエンド開発機能を提供します。Drupal 8、9、10、11+ 向けのカスタムモジュール開発、PHP プログラミング、API の使用、フック、プラグイン、サービス、データベース操作に関する包括的なガイダンスを提供します。
このスキルを使用する場面
以下の作業を行う場合にこのスキルを呼び出してください:
- カスタムモジュール開発: 新しい Drupal モジュールの作成
- フック: システムの動作を変更する Drupal フックの実装
- コントローラーとルーティング: カスタムページとルートの構築
- フォーム: 設定フォームやカスタムフォームの作成
- エンティティ: コンテンツエンティティまたは設定エンティティの操作
- プラグイン: ブロック、フィールドタイプ、フォーマッター、その他のプラグインの構築
- サービス: 再利用可能なビジネスロジックサービスの作成
- データベース操作: カスタムクエリとスキーマ定義
- API: Entity API、Form API、Database API、Plugin API
主な機能
1. カスタムモジュール開発
標準に準拠した完全な Drupal モジュールを作成します:
クイックスタートワークフロー:
assets/module-template/のモジュールテンプレートを使用しますMODULENAMEをマシン名(小文字、アンダースコア)に置き換えますMODULELABELを人間が読める名前に置き換えます- Drupal API を使用して機能を実装します
- モジュールを有効化してテストします:
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. Form 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;
}
開発ワークフロー
カスタムモジュールの作成
-
モジュールをスキャフォールドします:
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 -
モジュールファイルを更新します:
MODULENAMEをマシン名に置き換えますMODULELABELを読みやすい名前に置き換えます.info.ymlの依存関係を更新します- コントローラー、ルート、ロジックをカスタマイズします
-
有効化してテストします:
ddev drush en mymodule -y ddev drush cr -
反復的に開発します:
- 機能を実装します
- キャッシュをクリアします:
ddev drush cr - 十分にテストします
- ログを確認します:
ddev drush watchdog:tail
標準的な開発ワークフロー
- 計画: 必要な API/フックを特定します
- コーディング: Drupal API を使用して実装します
- テスト: モジュールを有効化し、キャッシュをクリアし、機能をテストします
- デバッグ: ログを確認し、必要に応じて Xdebug を使用します
- 改善: 最適化し、テストを追加し、コード品質を向上させます
ベストプラクティス
モジュール開発
- PSR-4 オートローディング: 名前空間の規約に従ってください
- 依存性注入: クラスでは DI を使用してください
- フック:
.moduleファイルにのみ実装してください - 型ヒント: 適切な PHP の型宣言を使用してください
- ドキュメント: PHPDoc ブロックを追加してください
- テスト: 自動テストを作成してください
コード品質
- Drupal コーディング標準: PHPCS のルールに従ってください
- セキュリティ: 入力を検証し、出力をサニタイズし、権限を確認してください
- パフォーマンス: 可能な場合はキャッシュし、クエリを最適化してください
- アクセシビリティ: フォームとページがアクセシブルであることを確保してください
- 国際化: すべての文字列に
$this->t()を使用してください
API の使用
- Entity API: 直接の DB クエリよりもエンティティ操作を優先してください
- Configuration: 設定には Config API を使用してください
- State API: 一時的またはエクスポート不要なデータに使用してください
- Cache API: 負荷の高い操作にはキャッシュを実装してください
- Queue 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
フックが動作しない
- フック名が正しいことを確認してください
- フックを追加した後はキャッシュをクリアしてください
- フックの順序が重要な場合はモジュールの weight を確認してください
クラスが見つからない
- PSR-4 名前空間がディレクトリと一致していることを確認してください
- キャッシュをクリアしてクラスレジストリを再構築してください
composer dump-autoloadでオートローディングを確認してください
データベースエラー
- スキーマ定義の構文を確認してください
- スキーマ変更後は
ddev drush updbを実行してください - クエリ内のテーブル名・カラム名を確認してください
リソース
リファレンスドキュメント
-
references/hooks.md- 例付きの一般的なフック- エンティティフック
- フォームフック
- ノードフック
- インストールフック
- トークンフック
-
references/module_structure.md- モジュールのパターン- コントローラーとルーティング
- フォーム(config とカスタム)
- プラグイン(ブロック、フィールドなど)
- サービスと DI
- イベントサブスクライバー
アセットテンプレート
assets/module-template/- モジュールのスキャフォールド.info.ymlメタデータ.moduleフックファイル.routing.ymlルート- コントローラーの例
バージョン互換性
Drupal 8 vs 9 vs 10 vs 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 ドキュメント