Claude Media
Claude CodeでEC-CUBEのプラグインを開発する — Symfony規約と互換の確かめ方

Claude CodeでEC-CUBEのプラグインを開発する — Symfony規約と互換の確かめ方

EC-CUBE 4のプラグイン開発をClaude Codeに任せる手順です。命名規則とバージョン別の要件をCLAUDE.mdに固定し、bin/consoleの出力で動作を確かめます。

EC-CUBE 4のプラグインは、Symfonyのサービスコンテナ、Doctrine、Twigの上に組まれています。Claude Codeに任せるときの勘所は、プラグイン固有の命名規則と対応バージョンを最初にCLAUDE.mdで固定し、bin/consoleの出力を見て動作を確かめさせることです。この記事では、その固定の仕方と確かめ方を、公式の開発者向けドキュメントの記述に沿って組み立てます。

なお、検索で先に出やすいdoc.ec-cube.netはEC-CUBE 3系のドキュメントです。4系はdoc4.ec-cube.netにあり、この記事の仕様はすべて後者に基づきます。3系の知識で書かれたコードをClaude Codeが出してきたときは、この違いが原因のことがあります。

EC-CUBE 4のプラグインは何でできているか

プラグインはapp/Plugin/<プラグインコード>/に置くディレクトリです。ドキュメントが示す一般的な構成から、主要な部分を抜き出すと次のとおりです。

app/Plugin/SamplePlugin/
├── Command
├── Controller
├── Entity
├── EventListener
│   └── EventListener.php
├── Form
├── PluginManager.php
├── Repository
├── Resource
│   ├── assets
│   ├── config
│   │   └── services.yaml
│   └── template
└── composer.json

すべてが必要なわけではなく、必須なのはcomposer.jsonだけです。コンテナの定義はResource/config/services.yamlに書きます。画像やCSSなどの静的ファイルはResource/assetsに置くと、インストールや更新のときにhtml/plugin/<プラグインコード>/assetsへコピーされます。Twigからは{{ asset('SamplePlugin/assets/sample.jpg', 'plugin') }}の形で参照します。

Claude Codeは、このディレクトリ構成を知らなければ、WordPressやLaravelの流儀でファイルを置きがちです。構成そのものを教えるより、「新しいクラスはこのディレクトリに置く」という置き場所の規則として渡すほうが効きます。

プラグイン用のCLAUDE.mdに何を固定するか

memoryのドキュメントでは、CLAUDE.mdは「毎回説明し直すことを書き留める場所」とされています。Claudeが同じ間違いを2回したときが、書き足すタイミングです。EC-CUBEのプラグインでは、次の3点が該当します。

  • 命名規則(コード、テーブル、ルート、コマンド、翻訳キー)
  • 対象とする本体バージョンとPHPの範囲
  • 触ってよい場所と触ってはいけない場所

次はそのまま使える最小の雛形です。Acmeは仮のベンダー名なので、自分の名前に置き換えてください。

# EC-CUBE 4 プラグイン開発(AcmeCategoryContent)
 
## 対象
- 対象の本体バージョン: 4.3(PHP 8.1〜8.3)
- プラグインコード: AcmeCategoryContent(ベンダー名+機能名のキャメルケース)
- スネークケース表記: acme_category_content
 
## 命名規則
- テーブル名: plg_acme_category_content_xxx
- 追加カラム(Trait): acme_category_content_xxx
- フロントのルート名: acme_category_content_xxx
- 管理画面のルート名: admin_acme_category_content_xxx
- コマンド名: acme_category_content:xxx:yyy
- 翻訳キー・パラメータ: acme_category_content.xxx.xxx
 
## 触ってよい場所
- app/Plugin/AcmeCategoryContent/ の中だけを編集する
- 本体のクラスとTwigは書き換えない。画面への差し込みは TemplateEvent の addSnippet() を使う
- 実行時に生成する設定ファイルは app/PluginData/AcmeCategoryContent/ に置く
 
## 確認コマンド
- キャッシュ削除: bin/console cache:clear --no-warmup
- ルート確認: bin/console debug:router
- イベント確認: bin/console debug:event-dispatcher

命名規則の列は、「プラグインで推奨の命名規則」のページをそのまま写したものです。コードは[ベンダー名][プラグイン]のキャメルケース、それ以外はプラグインコードのスネークケースが基準になります。

長くなったらrulesに分ける

CLAUDE.mdは1ファイルあたり200行以内に収めるのが目安です。長くなるなら、.claude/rules/に置いたルールファイルへpathsフロントマターで対象を絞れます。次の例は、プラグインのディレクトリを触るときだけ命名規則を読み込ませる書き方です。

---
paths:
  - "app/Plugin/AcmeCategoryContent/**"
---
 
# 命名規則
- テーブル名は plg_acme_category_content_ から始める
- 管理画面のルート名は admin_acme_category_content_ から始める

パス指定のルールは、Claudeが対象に合致するファイルを読み書きしたときに読み込まれます。CLAUDE.mdが「途中までしか読まれない」ときの上限は、Claude CodeでCLAUDE.mdが途中までしか読まれない上限と確認法で扱っています。書き方の型はCLAUDE.mdを実用に引き上げる10のパターンが参考になります。

対応バージョンを確かめる

プラグイン開発で一番事故になりやすいのは、本体のバージョンとPHPの組み合わせです。システム要件のページは、4.0・4.1、4.2、4.3でPHPの範囲を分けています。

本体PHP備考
4.0 / 4.1PHP7.3〜7.4備考4.0.0〜4.0.1は7.1〜7.2、4.0.2〜4.0.3は7.1〜7.3、4.0.4以降は7.1〜7.4
4.2PHP7.4〜8.1備考PostgreSQL 10〜14、MySQL 5.7または8.0
4.3PHP8.1〜8.3備考PostgreSQL 12〜16、MySQL 8.0〜8.4

4.2は7.4から8.1までを含むので、4.2と4.3のどちらにも入れたいプラグインは、PHP 8.1で動く書き方に揃えることになります。この「共通で動く範囲」を計算させるのは、Claude Codeに向いた作業です。CLAUDE.mdに対象バージョンの一覧を書いておき、新しい構文(列挙型やreadonlyなど)を使う前に範囲を確認させます。

composer.jsonで差が出る2か所

composer.jsonには、バージョンによって書き方が変わる項目が2つあります。プラグイン仕様のページに、次の違いが書かれています。

くらべる

composer.jsonのバージョン別の違い

name

パッケージ名

4.0系はec-cube/の後に大文字小文字を区別したプラグインコードを書きます。4.1系以降は、すべて小文字のプラグインコードです。

require

プラグインインストーラー

4.0系と4.1系は"~0.0.6 || ^2.0"、4.2系以降は"^2.0"をec-cube/plugin-installerに指定します。

extraのcodeにはプラグインコードを書き、typeはeccube-pluginにします。記載例では、名前がec-cube/productreview42、コードがProductReview42です。名前は小文字、コードはキャメルケースと、見た目が違うだけに、Claude Codeが取り違えやすい箇所です。生成させた後で、次のように突き合わせを頼みます。

app/Plugin/AcmeCategoryContent/composer.json を読んで、
name が小文字のプラグインコード(ec-cube/acmecategorycontent)、
extra.code が AcmeCategoryContent、type が eccube-plugin、
ec-cube/plugin-installer の制約が CLAUDE.md の対象バージョンに合っているか確認して。

4.0系を相手にするときの注意

4.0系は、プラグイン管理にComposer v1を使っています。Composer v1のメタデータは2025年8月1日以降に廃止されるとPackagistが告知した、とドキュメントにあります。通常は依存関係を更新しない限り影響しませんが、プラグインのインストールや更新で必要になる場合があるため、composer.jsonのrepositoriesを書き換える対処が案内されています。4.0系の保守案件では、この対処が済んでいる環境かどうかを、最初にClaude Codeに聞くのではなく、人が確認してCLAUDE.mdに書いておくほうが安全です。

bin/consoleの出力で動作を確かめる

CLAUDE.mdが「文脈であって強制ではない」ことは、memoryのドキュメントが明言しています。そこでコード生成の後は、コマンドの出力を根拠に確かめます。Claude Codeにはコマンドを実行させ、出力を読ませる流れが作れます。

手順

プラグインを作って確かめる流れ

  1. 1

    雛形を生成する

    bin/console eccube:plugin:generateが対話形式で名前、コード、バージョンを聞き、app/Plugin/<コード>を作ります。

  2. 2

    インストールする

    bin/console eccube:plugin:install --code=<コード>を実行します。成功すると[OK] Installed.が出て、管理画面のプラグイン一覧にも載ります。

  3. 3

    有効化してキャッシュを消す

    eccube:plugin:enableで有効化し、cache:clear --no-warmupでキャッシュを消します。

  4. 4

    ルートとイベントを見る

    debug:routerとdebug:event-dispatcherで、作ったコントローラーとリスナーが登録されたかを確かめます。

コマンドの一覧はphp bin/console listで、個別の使い方はphp bin/console <コマンド名> --helpで見られます。名前は先頭を省略でき、eccube:installはe:iでも動きます。

出力を判断材料にする頼み方

Claude Codeには「実装して」で終わらせず、確かめる手順まで頼みます。次のような依頼文にすると、生成と検証が1回のやり取りに収まります。

管理画面に設定画面を追加して。実装後に次の順で確認して、結果を報告すること。
1. bin/console cache:clear --no-warmup
2. bin/console debug:router の出力に admin_acme_category_content_config があるか
3. 無ければ、ルート定義のアノテーションと services.yaml を見直して原因を示す

ここで見ているのは、期待したルート名が一覧に現れるかどうかです。ルートが出ないなら、キャッシュが古いか、コントローラーがコンテナに登録されていません。Symfony拡張のページにも、コマンドや画面が反映されないときはcache:clear --no-warmupを実行するよう書かれています。

切り分け

ルートやコマンドが反映されないときの切り分け

  1. Qcache:clear --no-warmup を実行したか?
    答えが先にキャッシュを消す

    消したあとにもう一度debug:routerを見てください。

    答えがはい
  2. Qプラグインは有効化されているか?
    答えがeccube:plugin:enable を実行する

    ファイルを置いただけのプラグインはロードされず、dtb_pluginにレコードが登録されている必要があります。

    答えがはい

それでも出ないなら

クラスの置き場所とnamespace、services.yamlの定義をClaude Codeに照らし合わせさせます。

Entityとデータベースの更新

EC-CUBE 3.0.xからの変更点として、マイグレーションはdoctrine:schema:updateを使う方式に変わったとあります。PluginManagerでは初期データの投入、更新、削除だけを行い、スキーマ変更は持たせません。Claude Codeにはこの分担を守らせます。また、本体のEntityにカラムを足す拡張(EntityExtension)を使った場合は、eccube:generate:proxiesでプロキシファイルの生成が必要になります。

Twigは本体を直接書き換えず、スニペットで差し込む

画面への追加は、プラグインの中で一番Claude Codeが本体を書き換えたくなる場面です。方針は、既存のTwigを直接改修せず、TemplateEventのaddSnippet()でパーツを差し込むことです。これで本体の改修を避けられ、更新の影響を小さくできるとされています。

流れは2段です。まずEventSubscriberで、対象のTwig名にメソッドを結びつけます。

public static function getSubscribedEvents()
{
    return [
        'Shopping/index.twig' => 'onShoppingIndexTwig',
    ];
}
 
public function onShoppingIndexTwig(TemplateEvent $event)
{
    $event->addSnippet('@SamplePayment42/credit.twig');
}

次に、Resource/template/credit.twigへ差し込む断片を書きます。サンプル決済プラグインでは、断片の中のJavaScriptで差し込み位置を調整しています。addSnippet()の説明には、追加したコードは</body>タグの直前に出力されるとあります。

ここで押さえたいのは、昔の書き方が非推奨になっている点です。eccube.event.admin.requestのような、リクエストの前後に動くフックポイントは非推奨になりました。レンダリング後のHTMLにidやclassを頼りに介入する方式が、プラグイン間の競合を生んだためです。古い解説記事を読んだClaude Codeが、この非推奨の書き方を出してくることがあります。CLAUDE.mdには次の1行を足しておきます。

- 画面の差し込みは TemplateEvent の addSnippet() を使う。Responseを後から書き換えるフックは使わない

もう1つ、4系にはブロックを呼び出すTwig関数もあります。eccube_block_<ブロック名>()の形で、管理画面のブロック管理で作ったブロックをテンプレートの好きな位置に出せます。Twigファイルをブロック用のディレクトリに置いただけではブロックとして認識されず、ブロック管理での登録が必要です。

実装前に潰しておく落とし穴

落とし穴

命名規則ページの中で、管理画面のルート名が2通りある

ルーティングの節は管理画面のルート名をadmin_[コードのスネークケース]_xxxと示し、例はadmin_acme_category_content_configです。一方、URLの節にある管理画面コントローラーの例はacme_category_content_admin_configという名前です。Claude Codeに「公式ページに合わせて」とだけ頼むと、どちらかに揺れます。CLAUDE.mdでadmin_始まりに固定してください。

ほかにも、手を動かす前に決めておくと良い点があります。

  • 設定ファイルの置き場所: インストール後に生成・変更するファイルはapp/PluginData/<プラグインコード>に置きます。プラグインのディレクトリ内に置くと、更新や再インストールで失われます。
  • 削除の影響: プラグインを削除すると、app/Plugin/<プラグインコード>配下のファイルも物理的に消えます。Gitで管理していない作業コピーは戻せません。
  • 配布用のアーカイブ: オーナーズストアへの申請などにはtar.gzを使います。フォルダごと圧縮せず、.gitと.DS_Storeを含めないよう注意が書かれています。

削除は、Claude Codeに実行させたくない操作の筆頭です。permissionsのドキュメントにあるとおり、denyにはBash(git push *)のような範囲指定のルールを置けます。同じ書式で、.claude/settings.jsonにアンインストールを止める1行を足せます。

{
  "permissions": {
    "deny": [
      "Bash(php bin/console eccube:plugin:uninstall *)",
      "Bash(bin/console eccube:plugin:uninstall *)"
    ]
  }
}

実行するコマンドの形で一致が決まるので、phpを付ける書き方と付けない書き方の両方を入れてあります。

守らせたいことはhookにする

「コミット前に必ず確認する」のように、必ず実行したい処理は、CLAUDE.mdではなくhookに書くよう勧められています。hookはライフサイクルの決まったタイミングでシェルコマンドとして動き、Claudeの判断に左右されません。PHPのコード規約を保存のたびに直させる例は、WordPress規約をphpcsで直させる設定にあり、EC-CUBEでもphpcsやphpstanに置き換えて使えます。

汎用のプラグイン開発との違い

別のCMSでの流れは、Claude CodeでWordPressプラグイン開発を始めるにあります。WordPressはフックと関数名の接頭辞が衝突回避の軸でした。EC-CUBE 4はそれに加えて、Symfonyのコンテナ定義、Doctrineのスキーマ、本体バージョンごとのcomposer.jsonの差があります。確かめる対象が多いぶん、bin/consoleの出力を根拠にする流れの効果が大きくなります。

まとめ

EC-CUBE 4のプラグインをClaude Codeで開発するときは、書かせる前に決めておくことが成否を分けます。プラグインコードのキャメルケースとスネークケース、対象の本体バージョンとPHP、触ってよい範囲の3つをCLAUDE.mdに固定します。生成後はdebug:routerとdebug:event-dispatcherの出力で結果を確かめ、反映されないときはキャッシュ削除と有効化から疑います。画面の変更はスニペットで差し込み、削除系のコマンドはdenyで止めておく構成が、手戻りの少ない形です。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/memory
  2. code.claude.com/docs/en/permissions
  3. doc4.ec-cube.net/plugin_development
  4. doc4.ec-cube.net/plugin_spec
  5. doc4.ec-cube.net/plugin_naming_conventions
  6. doc4.ec-cube.net/quickstart/cli
  7. doc4.ec-cube.net/quickstart/requirement
  8. doc4.ec-cube.net/customize_symfony
この記事を共有:XはてブLinkedIn