Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .claude/skills/eccube-controller/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: eccube-controller
description: EC-CUBE 4.4 のコントローラを実装・改修するときの責務分離規約。「コントローラを作って」「アクションを追加して」「このコントローラを直して」「ルーティングを追加して」などと言われたとき、または src/Eccube/Controller・app/Customize/Controller 配下を作成・編集するときに使用する。Fat コントローラを避け業務ロジックを Service へ寄せるための規約。

---

# Controller 規約 — 責務分離と Fat 化防止(EC-CUBE 4.4)
Expand Down Expand Up @@ -135,6 +136,24 @@ vendor/bin/php-cs-fixer fix # PSR-12 整形・ライセン
- ❌ `#[Template]` 付きアクションの「エラー時に再描画される」を前提にレビュー判断 → ✅ `#[Template]` は配列を返したときのみ engage。Response/Redirect を返すパス(例: フォーム失敗で `redirectToRoute`)では描画されない
- ❌ 管理アクションを `%eccube_admin_route%` 配下以外に置く → ✅ admin ファイアウォール配下に置く
- ❌ 同一アクションで `executePurchaseFlow()` を複数回呼ぶとき 2 回目以降の `FlowResult` を無視する → ✅ 毎回 `hasError()`/`hasWarning()` の分岐を 1 回目と同じに揃える(共通化可)
- ❌ 配列が来る可能性のあるリクエスト値を `getString()` でスカラーに強制する → ✅ `InputBag` は非スカラーで例外を投げるため強制できない。`all()` で受けて型を検査する
- ❌ 削除アクションで外部キー違反だけを狙って個別の例外型を catch する → ✅ コアは `catch (\Exception)` で受けて `trans('admin.common.delete_error_foreign_key')` を返す形に統一している(`CategoryController` ほか 10 以上のコントローラ)。例外型を絞る実装は「動作としては正しいがコア非準拠」なので、揃えるかどうかは受入基準で判断する

## 実行・確認方法

```bash
# ルーティングが意図どおり登録されたか(#[Route] の属性ミスはここで気づく)
bin/console debug:router | grep product_detail
bin/console cache:clear

# 該当コントローラのテストだけを回す
vendor/bin/phpunit tests/Eccube/Tests/Web/ProductControllerTest.php
```

- 実装後の整形・型・静的解析・テストは **AGENTS.md「開発コマンド」** に従って実行する
(PHP-CS-Fixer / PHPStan level 6 / PHPUnit)。
- 認可の入り口は `security.yaml` だけでは追えない(`access_control` は `EccubeExtension` が動的注入する)。
新規の管理アクションは Skill `eccube-security` の「アクセス制御モデル」を確認する。

---

Expand Down
1 change: 1 addition & 0 deletions .claude/skills/eccube-e2e/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ description: EC-CUBE 4.4 の E2E テスト(Playwright・`e2e/` 配下)を実
- ❌ retry でプラグイン/データが残留し「既にインストール済み」で再失敗 → ✅ `beforeEach`/`afterEach` で cleanup(無効化→削除→ディレクトリ削除。`plugin-misc.spec.ts` 修正例)。
- ❌ パスワードを見た目の文字数で作る → ✅ NFKC 正規化後で 15 文字以上か数える(min15。`[...str.normalize('NFKC')].length` で確認。#6488)。
- ❌ 新規 `admin-*`/`front-*` spec を作ったのに CI で実行されない → ✅ `.github/workflows/e2e-test.yml` の `suite:` 配列にファイル名(接尾辞 `.spec.ts` 抜き)を追加する。
- ❌ 無関係な複数スイートが一斉に落ちたのを spec 側の不具合として個別に追う → ✅ 先に **globalSetup のログ**を見る。`setup-fixtures.php` は各ブロックに try-catch を持たない直列スクリプトなので、途中で Fatal になると**以降のフィクスチャが丸ごと未生成**になり、それに依存するスイートが連鎖的に落ちる。しかも `global-setup.ts` は失敗を catch して `Continuing without additional fixtures...` と警告するだけで**実行を止めない**ため、原因が spec 側にあるように見える。ログ末尾に `Fixtures setup complete.` が出ているかで切り分ける(コアの定数削除で `setup-fixtures.php` の置換が 1 箇所漏れ、admin-order / admin-refund-request / front-refund-request が同時に落ちた例がある)。

## 実行・確認方法

Expand Down
23 changes: 23 additions & 0 deletions .claude/skills/eccube-entity/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: eccube-entity
description: EC-CUBE 4.4 の Doctrine エンティティを実装・改修するときの規約。「エンティティを作って」「テーブルを追加して」「Entityにフィールドを足して」「リレーションを定義して」「マスタを追加して」などと言われたとき、または src/Eccube/Entity・app/Customize/Entity 配下を作成・編集するときに使用する。

---

# Entity 規約(EC-CUBE 4.4)
Expand Down Expand Up @@ -109,3 +110,25 @@ if (!class_exists(Example::class)) {
- ❌ 金額を float で四則演算(丸め誤差)→ ✅ `bcmath`(`bcadd` / `bcmul` / `bccomp`、スケール 2)で計算する
- ❌ `create_date` / `update_date` を自前の `#[ORM\PrePersist]`(+`#[ORM\HasLifecycleCallbacks]`)でセット → ✅ コアの `SaveEventSubscriber`(グローバル Doctrine prePersist/preUpdate)が `method_exists` で `setCreateDate`/`setUpdateDate`/`setCreator` を自動セットする(`src/Eccube/Doctrine/EventSubscriber/SaveEventSubscriber.php`)。setter さえ生やせばよく、自前 PrePersist は二重実装になるので書かない
- ❌ 他エンティティ(特にコアの `Product`/`Customer` 等、自分で制御できない親)への関連で親削除時の挙動を未決定 → ✅ FK は既定で削除を止める(RESTRICT 相当)。未指定だと**退会・商品削除が FK 違反で失敗**したり孤児化する。`onDelete`(`SET NULL`/`CASCADE`)を指定するか、Service・プラグイン disable 等で後始末する(コアは `onDelete` を限定使用し[95 JoinColumn 中 2 件]、多くは Service 側で関連を整理している)
- ❌ `@deprecated` なゲッタを未使用と判断して削除する → ✅ CSV 出力項目(`dtb_csv`)のアクセサとして現役のことがあり、削除は仕様変更になる。先に CSV 定義と照合する
- ❌ トレイト拡張・プロキシ絡みの不具合を「手元で再現しないから誤検知」と判断する → ✅ `TraitProxyAttributeDriver` は対象 FQCN が**まだ宣言されていないときだけ** `app/proxy/entity` の生成物を `require_once` する(同一 FQCN を二重宣言して `Cannot redeclare class` になるのを避けるため)。まっさらな状態では修正の有無にかかわらずトレイト由来の列が出るので、不具合はクラスとメタデータがロード済みのときだけ顕在化する。再現するには `getMetadataFor()` などで対象を先にロードした状態を作り、検証のたびに `app/proxy/entity` を消してから試す

## 実行・確認方法

```bash
# 属性から導かれるスキーマ差分を SQL で確認(実行前に必ず目視する)
bin/console doctrine:schema:update --dump-sql
bin/console doctrine:schema:update --force

# トレイトで拡張した場合はプロキシを再生成する(忘れると列が生えない)
bin/console eccube:generate:proxies
bin/console cache:clear
```

- 実装後の整形・型・静的解析・テストは **AGENTS.md「開発コマンド」** に従って実行する
(PHP-CS-Fixer / PHPStan level 6 / PHPUnit)。
- カラム追加はマイグレーション不要(`schema:update` が反映する)。要否の判断は Skill `eccube-migration` を参照。

---

実装・改修後は、Skill `eccube-review-responsibility` で責務分離を点検すること。
20 changes: 20 additions & 0 deletions .claude/skills/eccube-formtype/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: eccube-formtype
description: EC-CUBE 4.4 のフォーム(FormType)を実装・改修するときの規約。「フォームを作って」「FormTypeを追加して」「入力項目を足して」「バリデーションを設定して」「検索フォームを作って」「既存フォームに項目を追加して」などと言われたとき、または src/Eccube/Form・app/Customize/Form 配下を作成・編集するときに使用する。

---

# FormType 規約(EC-CUBE 4.4)
Expand Down Expand Up @@ -79,3 +80,22 @@ class ExampleType extends AbstractType
- ❌ 具象クラス依存 → ✅ コンストラクタ DI + 必要なサービスの注入
- ❌ 既存フォームに二重送信防止/楽観ロック用の unmapped hidden を足し、サーバー側で値未送信を即エラー扱い → ✅ 値が空/未送信なら判定をスキップ(プログラム的 POST・既存テスト・外部連携を壊さない後方互換を保つ)
- ❌ 共通 FormType(RepeatedPasswordType 等)を子で使い `options.constraints` を渡す(親が定義した制約が全置換され消える) → ✅ 親の制約一式も再掲して付与する

## 実行・確認方法

```bash
# FormType の構成・オプション・拡張が効いているかを確認する(FQCN でも短縮名でもよい)
bin/console debug:form BlockType
bin/console cache:clear

# フォームのテストだけを回す
vendor/bin/phpunit tests/Eccube/Tests/Form/Type/Admin/BlockTypeTest.php
```

- 実装後の整形・型・静的解析・テストは **AGENTS.md「開発コマンド」** に従って実行する
(PHP-CS-Fixer / PHPStan level 6 / PHPUnit)。
- `FormTypeExtension` で拡張した場合は、`debug:form` の出力に追加項目が現れることで登録を確認できる。

---

実装・改修後は、Skill `eccube-review-responsibility` で責務分離を点検すること。
32 changes: 30 additions & 2 deletions .claude/skills/eccube-migration/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: eccube-migration
description: EC-CUBE 4.4 のデータベースマイグレーションを作成・編集するときの規約。「マイグレーションを作って」「マスタデータ/初期データを投入したい」「カラムの型を変えたい/リネームしたい」「スキーマを変えたい」などと言われたとき、または app/DoctrineMigrations 配下を作成・編集するときに使用する。注意: 単純なカラム追加は Entity 属性+schema:update で反映されるためマイグレーション不要(その判断にも本 Skill を参照)。

---

# マイグレーション規約(EC-CUBE 4.4)
Expand Down Expand Up @@ -32,6 +33,11 @@ bin/console doctrine:migrations:migrate --no-interaction
Entity 属性を足せば、新規は `schema:create`、既存は `schema:update` が拾う。
→ 例: PR #4912(Google アナリティクス機能)は `BaseInfo` にカラムを追加したが、
ALTER マイグレーションは作らず `schema:update` に委ねている。
> **ただし実際のコアには両方の前例がある**。`Version20260316234241` は `dtb_base_info` への
> カラム追加に対して `ALTER TABLE dtb_base_info ADD ...` を書いている。
> つまり「カラム追加にマイグレーションが付いている=規約違反」とは言い切れない。
> レビューで「マイグレーション欠落」「マイグレーション不要」を**断定しない**こと。
> 既定は不要(`schema:update` に委ねる)だが、既存行へ既定値を確実に入れたい等の理由があれば付けてよい。
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- **マイグレーションを書くのは次の場合に限る**:
1. **マスタ/初期データの INSERT**(`mtb_*` のレコード、`dtb_block` / `dtb_mail_template` / `dtb_csv` 等への初期レコード投入)。
2. **`schema:update` が安全に扱えない構造変更**(カラムの**型変更・リネーム**、データ移行を伴う変更、
Expand Down Expand Up @@ -120,10 +126,32 @@ final class Version20240101000000 extends AbstractMigration

## よくある間違い

- ❌ カラムを足したので `ALTER TABLE ... ADD COLUMN` のマイグレーションを書く
→ ✅ Entity 属性を足すだけ。新規は `schema:create`、既存は `schema:update --force` が反映する。
- ❌ カラムを足したので反射的に `ALTER TABLE ... ADD COLUMN` のマイグレーションを書く
→ ✅ 既定は Entity 属性を足すだけ(新規は `schema:create`、既存は `schema:update --force` が反映)。
既存行への既定値投入など理由があれば書いてよい(上記「両方の前例がある」を参照)。
- ❌ `doctrine:migrations:diff` で Entity 差分から ALTER を自動生成する
→ ✅ `doctrine:migrations:generate` で空の雛形を作り、必要な SQL(INSERT・型変更等)だけ手で書く。
- ❌ マイグレーションでテーブルを"新規定義"してスキーマの源泉にする → ✅ 源泉は Entity 属性。
- ❌ INSERT・構造変更でガードなし → 再実行や環境差で失敗。✅ 存在チェックで冪等にする。
- ❌ `down()` 未実装 → ロールバック不能。✅ `up()`/`down()` を対で実装。

## 実行・確認方法

```bash
# 属性から導ける差分(カラム追加・変更)は schema:update 側で反映される
bin/console doctrine:schema:update --dump-sql

# マイグレーション(INSERT・型変更等)を適用し、down() も往復で確かめる
bin/console doctrine:migrations:migrate
bin/console doctrine:migrations:migrate prev
bin/console doctrine:migrations:migrate
```

- 実装後の整形・型・静的解析・テストは **AGENTS.md「開発コマンド」** に従って実行する
(PHP-CS-Fixer / PHPStan level 6 / PHPUnit)。
- 冪等性は「同じマイグレーションを 2 回流しても失敗しない」ことで確認する。
- 新規インストール経路(`doctrine:schema:create` + 初期データ)でも通ることを確認する。

---

実装・改修後は、Skill `eccube-review-responsibility` で責務分離を点検すること。
1 change: 1 addition & 0 deletions .claude/skills/eccube-plugin/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ bin/console eccube:generate:proxies # app/proxy/entity/ を再生成
- ❌ トレイト追加後にプロキシ再生成を忘れる → ✅ `bin/console eccube:generate:proxies`
- ❌ プロジェクト固有の 1 回限りの改変をプラグイン化 → ✅ それは `app/Customize/`。着脱・再配布するものだけプラグイン
- ❌ `app/Customize`(`Eccube\` を直接拡張)と `app/Plugin`(`Plugin\{Code}\` 独立名前空間)の名前空間を混同 → ✅ 置き場所で名前空間を使い分ける
- ❌ 「無効化した(または DB に登録していない)プラグインは読み込まれない」と考える → ✅ `Kernel::registerBundles()` は **DB の有効/無効を一切見ず**、`app/Plugin` 直下を `Finder` で列挙して各 `app/Plugin/<コード>/Resource/config/bundles.php` を `require` する。依存クラスが欠けたプラグインを置いただけで**カーネル起動そのものが失敗し、`bin/console` も PHPUnit も全滅する**。QA が一斉に起動段階で落ちたら、composer の依存を疑う前に `app/Plugin` の中身を退避してキャッシュを消して切り分ける

## 実行・確認方法

Expand Down
4 changes: 4 additions & 0 deletions .claude/skills/eccube-purchase-flow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ description: EC-CUBE 4.4 の受注処理(PurchaseFlow の Processor/Validator
メソッドを実装する**。PurchaseProcessor は `AbstractPurchaseProcessor` を継承すれば必要なメソッドだけ override 可。
- **`supports()` で早期 return**: フロー種別・`Order` か否か・店舗設定(`BaseInfo`)で適用可否を判定し、
対象外なら何もしない(`AddPointProcessor::supports()` が手本)。
- **トランザクション境界はリクエスト全体**: `TransactionListener` が `kernel.request` で `beginTransaction()`、
`kernel.terminate` で `commit()`、例外時に `rollback()` する。Processor 内の `flush()` は SQL を発行するだけで
**コミットではない**ため、在庫引当・採番の可視性や競合を論じるときは「flush 済み=確定」と読み替えないこと。
- **金額計算は `bcmath`**(`bcadd` / `bcsub` / `bcmul` / `bccomp`)。float 演算で組まない。
合計・税・送料・値引きの集計は `PurchaseFlow::calculateAll()` が各段階後に行うので、Processor 側は
**明細(Item)を足し引きする**ことに集中する(合計の手計算は不要)。
Expand Down Expand Up @@ -237,6 +240,7 @@ class SaleLimitOneValidator extends ItemValidator
- ❌ PurchaseProcessor の `rollback()` を実装し忘れる → ✅ `prepare()` の逆操作(在庫戻し等)を必ず実装する
- ❌ 属性方式で実行順を制御しようとする → ✅ 順序が要るなら YAML タグの `priority`(降順)で指定する
- ❌ (A) YAML タグと (B) 属性を両方付ける → ✅ どちらか一方。コアは YAML、プラグイン/Customize は属性が定石
- ❌ 送料無料を商品単位の性質として扱う(商品詳細に「この商品は送料無料」と出す、構造化データに `shippingRate: 0` を出す等)→ ✅ `DeliveryFeeFreePreprocessor` は `BaseInfo::getDeliveryFreeAmount()` を `$itemHolder->getTotal()` と、`getDeliveryFreeQuantity()` を `getQuantity()` と比較する **カート全体の合計に対する条件**。カートが確定していない商品ページでは判定できないので、出せない値は出さない

## 実行・確認方法

Expand Down
19 changes: 19 additions & 0 deletions .claude/skills/eccube-repository/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: eccube-repository
description: EC-CUBE 4.4 の Doctrine リポジトリを実装・改修するときの規約。「リポジトリを作って」「検索メソッドを追加して」「クエリを書いて」「一覧の絞り込みを実装して」などと言われたとき、または src/Eccube/Repository・app/Customize/Repository 配下を作成・編集するときに使用する。

---

# Repository 規約(EC-CUBE 4.4)
Expand Down Expand Up @@ -74,3 +75,21 @@ class ExampleRepository extends AbstractRepository
- ❌ 画面表示の一覧・関連取得を無制限に全件取得(件数が際限なく増え得る)→ ✅ ページング(Paginator 用に QueryBuilder を返す)か上限を設ける
- ❌ join 先への絞り込みを EXISTS 部分クエリへ移すとき、その別名に掛かっていた既存の制約を引き継がない → ✅ 同じ制約を EXISTS 内に再掲し、集計・出力側の母集団と一致させる
- ❌ 1 対多の範囲絞り込みで下限・上限を独立した EXISTS 2 本に分ける(別々の子行が満たせばヒットしてしまう)→ ✅ 同一の子行に両条件を要求するなら EXISTS 1 本にまとめる

## 実行・確認方法

```bash
# 生成される DQL / SQL を確認する(EXISTS の制約漏れ・JOIN の重複はここで気づく)
# → $qb->getQuery()->getDQL() / ->getSQL() をテストで出力して目視する
vendor/bin/phpunit tests/Eccube/Tests/Repository/ProductRepositoryTest.php
```

- 実装後の整形・型・静的解析・テストは **AGENTS.md「開発コマンド」** に従って実行する
(PHP-CS-Fixer / PHPStan level 6 / PHPUnit)。
- 件数・母集団が絡む変更は、**期待件数、または想定する母集団との整合性**をテストで固定する
(絞り込み条件を変えれば件数が変わるのは正常なので、「変更前後で件数が一致すること」を固定してはいけない)。
- 一覧のページングは Paginator 側で適用されるため、Repository は QueryBuilder を返したままにする。

---

実装・改修後は、Skill `eccube-review-responsibility` で責務分離を点検すること。
Loading
Loading