プラグイン規約(EC-CUBE 4.4)
対象: app/Plugin/{PluginCode}/**(コア側の仕組みは src/Eccube/Plugin/, src/Eccube/Service/PluginService.php)
前提: Symfony 7.4 / PHP 8.2+
目的: 自己完結したパッケージとして機能を追加し、コアやプロジェクト固有カスタマイズ(
app/Customize/)と混同しないこと。 プロジェクト固有の 1 回限りの改変はapp/Customize/、再配布・着脱可能な機能はapp/Plugin/。
雛形の生成(まず CLI で骨組みを作る)
新規プラグインは手書きで一から作らず、コアの生成コマンドで雛形を作るのが定石。
bin/console eccube:plugin:generate <name> <code> <ver>
# 例: bin/console eccube:plugin:generate "My Plugin" Example 1.0.0
app/Plugin/{code}/ に骨組み一式が生成される(src/Eccube/Command/PluginGenerateCommand.php):
composer.json ・ 管理画面の Controller/Admin/ConfigController.php ・ Entity/Config.php(plg_{code}_config)・
Repository/ConfigRepository.php ・ Form/Type/Admin/ConfigType.php ・ Resource/template/admin/config.twig ・
TwigBlock.php / Nav.php / Event.php ・ Resource/locale/messages.ja.yaml 等 ・
.github/workflows/release.yml ・ .gitattributes。
- 引数は
name(表示名)/code(PluginCode)/ver(composer.json の version) の順(位置引数)。 - 生成物は Config 画面・Entity 込みのフル構成。使わないファイルは削ってよい(残すべき最小は下記)。
codeは^\w+$(後述の制約)。PluginManager.phpは生成されないので、ライフサイクル処理が要るときは下記に従い手で足す。
開発時の置き場所(事故防止・重要):
app/Plugin/{code}/直下で直接開発すると、プラグイン削除(uninstall)のテストをした瞬間にソースごと消える。 実開発では別ディレクトリで開発し、シンボリックリンクで配置するのが安全。コアの local path リポジトリ機能を使う:bin/console eccube:composer:require <パッケージ名> --from <別ディレクトリのパス>
--fromで指定したローカルパスを composer リポジトリとして登録し、app/Plugin/へシンボリックリンクで取り込む(ComposerRequireCommandの--fromオプション。参考: PR #5843)。
プラグインの最小構成と配置
プラグインは必ず app/Plugin/{PluginCode}/ に置く。PSR-4 で Plugin\{PluginCode}\ = app/Plugin/{PluginCode}/。
generate が作る雛形から不要分を削ると、最終的に残すべきは次の構成(手書きするときもこれが下限)。
app/Plugin/{PluginCode}/
├── composer.json # 必須
└── PluginManager.php # 任意(ライフサイクル処理が要るときだけ)
- PluginCode は
^\w+$(英数字とアンダースコアのみ)。ディレクトリ名・名前空間・クラス名に使われるため厳格。-は不可。 composer.jsonの必須はversionとextra.code。extra.codeが無いと install で失敗する。 推奨:name(ec-cube/xxx),description,type: "eccube-plugin",requireにec-cube/plugin-installer。
{
"name": "ec-cube/example",
"version": "1.0.0",
"description": "...",
"type": "eccube-plugin",
"require": { "ec-cube/plugin-installer": "*" },
"extra": { "code": "Example" }
}
ライフサイクル(PluginManager)
ライフサイクル処理が必要なときだけ Plugin\{Code}\PluginManager(クラス名は固定)を AbstractPluginManager を継承して作る。
5 メソッドはすべてデフォルト no-opなので、必要なものだけ override すればよい。
| メソッド | 呼ばれる契機 | 用途の例 |
|---|---|---|
install |
インストール時(postInstall 経由) | 初期データ投入 |
enable |
有効化時 | マイグレーション適用 |
disable |
無効化時 | マイグレーションを戻す |
update |
更新時 | 差分マイグレーション |
uninstall |
アンインストール時(initialized 済みのみ) | クリーンアップ |
namespace Plugin\Example;
use Eccube\Plugin\AbstractPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
class PluginManager extends AbstractPluginManager
{
public function enable(array $meta, ContainerInterface $container): void
{
// マイグレーション適用(AbstractPluginManager::migration を利用)
$conn = $container->get('doctrine')->getManager()->getConnection();
$this->migration($conn, $meta['code']);
}
}
- メソッドのシグネチャは
(array $meta, ContainerInterface $container)。$meta['code']は composer.json のextra.code。 - install 直後はデフォルト無効(enabled=false)。有効化は
eccube:plugin:enableコマンドか管理画面で行う(無効化はコンソールコマンドが無く、管理画面から行う)。
拡張パターン(プラグインから何を足すか)
| 拡張 | 置き場所 / 名前空間 | 作法 | 参照 Skill |
|---|---|---|---|
| エンティティ拡張 | Plugin\{Code}\Entity\*Trait |
トレイトに #[EntityExtension(\Eccube\Entity\Target::class)] を付け、#[ORM\Column] でカラム追加 |
eccube-entity |
| コントローラ追加 | Plugin\{Code}\Controller |
#[Route] 属性でルーティング |
eccube-controller |
| フォーム拡張 | Plugin\{Code}\Form\Extension |
AbstractTypeExtension を継承し getExtendedTypes() で対象指定 |
eccube-formtype |
| リポジトリ拡張 | Plugin\{Code}\Repository |
— | eccube-repository |
| イベント購読 | Plugin\{Code}\EventListener 等 |
EventSubscriberInterface(autoconfigure で自動登録) |
eccube-event-subscriber |
| 受注処理の拡張 | Plugin\{Code}\Service\PurchaseFlow\Processor |
#[CartFlow] / #[ShoppingFlow] / #[OrderFlow] 属性で対象フローへ自動登録 |
eccube-service |
| マイグレーション | Plugin\{Code}\DoctrineMigrations\Version* |
AbstractMigration を継承(テーブルは migration_{code} で管理) |
eccube-migration |
// エンティティ拡張の例: app/Plugin/Example/Entity/CustomerExampleTrait.php
namespace Plugin\Example\Entity;
use Doctrine\ORM\Mapping as ORM;
use Eccube\Attribute\EntityExtension;
#[EntityExtension(\Eccube\Entity\Customer::class)]
trait CustomerExampleTrait
{
#[ORM\Column(name: 'example_no', type: 'smallint', nullable: true)]
public $example_no;
}
プロキシ再生成(忘れやすい急所)
エンティティ拡張(トレイト)を足したら プロキシの再生成が必要。
enable/disable/uninstall 時はコア(PluginService)が自動で再生成するが、開発中に手で確認するときは明示実行する:
bin/console eccube:generate:proxies # app/proxy/entity/ を再生成
トレイトに #[EntityExtension] を付け忘れるとプロキシに反映されず、カラムが認識されない。
よくある間違い
- ❌ 雛形を手で一から作る → ✅
bin/console eccube:plugin:generate <name> <code> <ver>で骨組みを生成し、不要分を削る - ❌
composer.jsonにextra.codeが無い → ✅ 必須。無いと install で失敗 - ❌ PluginCode に
-を使う → ✅^\w+$(英数字・アンダースコアのみ) - ❌ install しただけで動くと思う → ✅ install 直後は無効。
eccube:plugin:enable --code=...で有効化 - ❌ エンティティトレイトに
#[EntityExtension(Target::class)]を付け忘れ → ✅ 付けないとプロキシに乗らない - ❌ トレイト追加後にプロキシ再生成を忘れる → ✅
bin/console eccube:generate:proxies - ❌ プロジェクト固有の 1 回限りの改変をプラグイン化 → ✅ それは
app/Customize/。着脱・再配布するものだけプラグイン - ❌
app/Customize(Eccube\を直接拡張)とapp/Plugin(Plugin\{Code}\独立名前空間)の名前空間を混同 → ✅ 置き場所で名前空間を使い分ける
実行・確認方法
コンソール・QA ツール(PHPUnit / PHPStan / PHP-CS-Fixer)の実行方法は AGENTS.md「開発コマンド」を参照。
bin/console eccube:plugin:generate "My Plugin" Example 1.0.0 # 雛形生成(name code ver)
bin/console eccube:plugin:install --code=Example # 既存ディレクトリからインストール
bin/console eccube:plugin:enable --code=Example # 有効化
bin/console eccube:plugin:update Example # 更新(PluginManager::update を呼ぶ)
bin/console eccube:generate:proxies # プロキシ再生成
bin/console doctrine:schema:validate # スキーマ整合確認
- 状態確認は
dtb_pluginテーブル(code/enabled/initialized)とapp/proxy/entity/を見る。
実装・改修後は、各レイヤの Skill(eccube-entity / eccube-controller / eccube-formtype / eccube-migration 等)と
eccube-review-responsibility で責務分離を点検すること。