app/Customize 規約(EC-CUBE 4.4)
対象: app/Customize/**, app/template/**(テンプレート上書き)
前提: Symfony 7.4 / PHP 8.2+ / Doctrine ORM 3.x
対象 / 前提
app/Customize/ は プロジェクト固有の改変 を置く場所。コア(src/Eccube/)を直接書き換えず、
ここで拡張・上書きすることで、コアを直接書き換えずに改変するのが目的。
注意: 「アップグレード安全」は限定的(過信しない)
- 影響を受けにくいのは パッチバージョンの更新まで。マイナー/メジャー更新ではコア側の変更で破綻し得る。
- コアエンティティそのものは上書き(置換)できない。拡張は trait+
#[EntityExtension]による「追加」のみ(既存カラム/メソッドの差し替えは不可)。- サービス/テンプレートを
app/Customizeで override すると、コアに当たった脆弱性パッチ・修正が自動では反映されず、個別に再適用が必要になる。override は最小限にし、慎重に使う。
- PSR-4 で **
Customize\=app/Customize/**(composer.jsonのautoload.psr-4)。 app/config/eccube/services.yamlでCustomize\名前空間は **autowire / autoconfigure 済み**として登録される (_defaultsがautowire: true/autoconfigure: true)。Customize\Controller\はcontroller.service_argumentsタグ付き。- 除外:
Customize\のサービス登録は{Entity,Resource,Tests}を除外する(Entity はサービスではないため)。
plugin との使い分け(混同しない)
app/Customize/(本 Skill) |
app/Plugin/{Code}/(Skill eccube-plugin) |
|
|---|---|---|
| 名前空間 | Customize\ |
Plugin\{Code}\(独立名前空間) |
| 想定 | プロジェクト固有・1 回限りの改変 | 着脱・再配布できる機能パッケージ |
| ライフサイクル | なし(常時有効) | install/enable/disable/uninstall あり |
| メタデータ | なし | composer.json の extra.code 必須 |
プロジェクト固有の改変は
app/Customize/、着脱・再配布するものはapp/Plugin/。 エンティティ拡張・フォーム拡張・proxy 再生成といった作法そのものは両者で共通(trait+#[EntityExtension]等)。
基本ルール
- PHP ファイル先頭に EC-CUBE ライセンスヘッダ。型宣言を付ける(PHPStan level 6)。
- 依存はコンストラクタインジェクション(autowire が効く)。
- 各レイヤの作法は対応 Skill に従う(
eccube-entity/eccube-formtype/eccube-controller/eccube-service/eccube-repository)。 本 Skill は 「Customize ならではの置き場所と上書き方法」 に絞る。
実装パターン
1. エンティティ拡張(コアエンティティにフィールド追加)
コアを書き換えず、app/Customize/Entity/ に trait を置き、#[EntityExtension(対象::class)] を付ける。
Eccube\Attribute\EntityExtension は TARGET_CLASS | IS_REPEATABLE の属性で、value(対象エンティティの FQCN)を取る。
// app/Customize/Entity/ProductTrait.php
namespace Customize\Entity;
use Doctrine\ORM\Mapping as ORM;
use Eccube\Attribute\EntityExtension;
#[EntityExtension(\Eccube\Entity\Product::class)]
trait ProductTrait
{
#[ORM\Column(name: 'custom_note', type: 'string', length: 255, nullable: true)]
private ?string $customNote = null;
public function getCustomNote(): ?string
{
return $this->customNote;
}
public function setCustomNote(?string $customNote): self
{
$this->customNote = $customNote;
return $this;
}
}
- trait を足したら proxy 再生成(
bin/console eccube:generate:proxies)でapp/proxy/entity/に反映される。#[EntityExtension]を付け忘れると proxy に乗らずカラムが認識されない。 - カラムを足すだけならマイグレーション不要(属性が源泉。
schema:update --forceが反映)。詳細は Skilleccube-entity/eccube-migration。
2. フォーム拡張(既存フォームに項目追加)
Symfony\Component\Form\AbstractTypeExtension を継承し、getExtendedTypes() で対象 FormType を返す。
app/Customize/Form/Extension/ に置く(autoconfigure で form.type_extension として自動登録される)。
// app/Customize/Form/Extension/ProductTypeExtension.php
namespace Customize\Form\Extension;
use Eccube\Form\Type\Admin\ProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class ProductTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('custom_note', TextType::class, [
'required' => false,
'mapped' => true, // エンティティ拡張のプロパティに紐づける場合
]);
}
public static function getExtendedTypes(): iterable
{
return [ProductType::class];
}
}
- 実例(コア側の AbstractTypeExtension):
src/Eccube/Form/Extension/HelpTypeExtension.php(getExtendedTypes()でFormType::classを返し全フィールドを拡張)。 - 追加項目を画面に出すには、対応するテンプレート側にも出力を足す(下記 4)。詳細は Skill
eccube-formtype。
3. サービスの上書き / デコレーション
EC-CUBE は #[AsDecorator] 属性は使っていない(コアに用例なし)。
app/config/eccube/services.yaml に明示的な定義を足し、Symfony のデコレーション(decorates)で包むのが基本。
# app/config/eccube/services.yaml に追記
services:
Customize\Service\MyCartServiceDecorator:
decorates: Eccube\Service\CartService
# 元サービスは .inner で受け取る(コンストラクタ DI)
arguments:
$inner: '@.inner'
// app/Customize/Service/MyCartServiceDecorator.php
namespace Customize\Service;
use Eccube\Service\CartService;
class MyCartServiceDecorator
{
public function __construct(private CartService $inner)
{
}
// 必要なメソッドだけ振る舞いを変え、それ以外は $this->inner に委譲する
}
decorates/decoration_priority/decoration_inner_name等のサービスキーが使える (app/config/eccube/reference.phpの DefaultsType/InstanceofType に定義あり)。- 単純に同名サービス ID で置き換えたい場合は、
services.yamlで同じクラス ID にclass:を上書き定義する手もあるが、 元の振る舞いを残したい拡張は デコレーションが安全。元クラスへ依存している箇所を壊さないよう、型は元サービスを満たすこと。 - ロジックの責務分離は Skill
eccube-service。コントローラを追加する場合はapp/Customize/Controller/に置く (routes.yamlのcustomize_controllersがtype: attributeで#[Route]を走査、services.yaml でcontroller.service_argumentsタグ付き)。
4. テンプレート上書き
app/template/ の コアと同じ相対パスに同名ファイルを置くと上書きできる(app/config/eccube/packages/twig.yaml の paths)。
Twig の検索パスは app/template/...(eccube_theme_front_dir / eccube_theme_admin_dir)がコア既定(*_default_dir)より先に登録されているため、同名なら app 側が優先される。
| 対象 | 置き場所(上書き先) | コア原本 |
|---|---|---|
| 店頭(フロント) | app/template/{テーマコード}/...(eccube.theme = ECCUBE_TEMPLATE_CODE、既定 default) |
src/Eccube/Resource/template/default/... |
| 管理画面 | app/template/admin/...(Twig 名前空間 admin) |
src/Eccube/Resource/template/admin/... |
- 例: フロントの
Product/detail.twigを変えるならapp/template/{テーマコード}/Product/detail.twigにコピーして編集。 - 一部だけ差し込みたい場合は、コア改変や全文コピーより テンプレートイベントで挿入する方が壊れにくい(Skill
eccube-event-subscriber/eccube-twig-template)。 - XSS・
rawの扱いは Skilleccube-twig-template。
よくある間違い
- ❌ コア(
src/Eccube/)を直接書き換える → ✅app/Customize/で拡張・上書きし、アップグレード安全にする - ❌ プロジェクト固有の 1 回限りの改変をプラグイン化 → ✅ それは
app/Customize/。着脱・再配布するものだけapp/Plugin/ - ❌ 名前空間を
Plugin\{Code}\と混同 → ✅ Customize はCustomize\=app/Customize/ - ❌ エンティティ拡張の trait に
#[EntityExtension(対象::class)]を付け忘れ → ✅ 付けないと proxy に乗らずカラムが認識されない - ❌ trait 追加後に proxy 再生成を忘れる → ✅
bin/console eccube:generate:proxies - ❌ カラム追加に ALTER マイグレーションを書く → ✅ 属性が源泉。
schema:update --forceが反映(マイグレーションは INSERT・型変更等に限る。Skilleccube-migration) - ❌ 既存フォームを直接改変 → ✅
AbstractTypeExtension+getExtendedTypes()(app/Customize/Form/Extension/)で拡張 - ❌ サービスを
#[AsDecorator]で包む(コアの作法と不一致) → ✅services.yamlでdecorates+@.inner委譲 - ❌ テンプレートを上書こうとしてコア原本側を編集 → ✅
app/template/に同じ相対パスで同名ファイルを置く(app 側が優先) - ❌ 上書きパスのテーマ名を間違える → ✅ フロントは
app/template/{ECCUBE_TEMPLATE_CODE}/(既定default)、管理画面はapp/template/admin/
実行・確認方法
コンソール・QA ツール(PHPUnit / PHPStan / PHP-CS-Fixer)の実行方法は AGENTS.md「開発コマンド」を参照。
bin/console eccube:generate:proxies # エンティティ拡張(trait)を足したら proxy 再生成
bin/console doctrine:schema:update --dump-sql # 追加カラムの差分プレビュー
bin/console doctrine:schema:update --force # 属性差分を反映(単純なカラム追加)
bin/console cache:clear # services.yaml / テンプレート上書きの反映確認
bin/console doctrine:schema:validate # スキーマ整合確認
- サービス上書きの反映は
bin/console debug:container <ID>/debug:autowiringで確認できる。 - 追加・改修後は各レイヤ Skill(
eccube-entity/eccube-formtype/eccube-service/eccube-controller/eccube-twig-template)とeccube-review-responsibilityで責務分離・セキュリティを点検する。