# 画面設計書 34-カテゴリ管理

## 概要

本ドキュメントは、Shopware 6 管理画面（Administration）のカテゴリ管理画面の設計仕様を定義します。この画面は商品カテゴリを階層構造（ツリー形式）で管理するための機能を提供し、カテゴリの作成、編集、削除、並び替え、およびCMSレイアウトの割り当てを行います。

### 本画面の処理概要

**業務上の目的・背景**：カテゴリ管理画面は、ECショップの商品分類体系を構築・維持するための重要な機能です。適切なカテゴリ構造は顧客が商品を見つけやすくし、SEO効果を高め、売上向上に貢献します。また、カテゴリごとにCMSページを設定することで、ブランドイメージに合わせたカスタマイズされた商品表示が可能になります。

**画面へのアクセス方法**：左側メインナビゲーションの「カタログ」>「カテゴリ」をクリックしてアクセスします。URLパスは `/admin#/sw/category/index` です。権限 `category.viewer` が必要です。

**主要な操作・処理内容**：
1. カテゴリツリーの表示：階層構造でカテゴリを表示
2. カテゴリの作成：新規カテゴリの追加（ルートまたは子カテゴリ）
3. カテゴリの編集：名前、説明、メタ情報、CMSレイアウト等の編集
4. カテゴリの移動：ドラッグ＆ドロップで階層変更
5. カテゴリの削除：不要カテゴリの削除
6. ランディングページ管理：カテゴリとは別にランディングページも管理可能
7. エントリーポイント設定：販売チャネルのナビゲーション、フッター、サービスカテゴリの設定

**画面遷移**：カテゴリツリーでカテゴリを選択すると右側に詳細編集画面が表示されます。CMSレイアウト編集でCMS管理画面に遷移できます。商品タブから商品一覧に遷移できます。

**権限による表示制御**：
- `category.viewer`: カテゴリ一覧の閲覧
- `category.editor`: カテゴリの編集
- `category.creator`: 新規カテゴリ作成
- `category.deleter`: カテゴリ削除
- `landing_page.viewer`: ランディングページの閲覧

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 58 | カテゴリページ表示 | 主機能 | 商品カテゴリの階層管理 |

## 画面種別

詳細（ツリー形式+詳細ペイン）

## URL/ルーティング

- **URLパス**: `/admin#/sw/category/index`
- **詳細ページ**: `/admin#/sw/category/index/:id`
- **ルート名**: `sw.category.index`, `sw.category.detail`
- **モジュール名**: `sw-category`
- **エンティティ**: `category`

### 子ルート（詳細タブ）

| ルート名 | パス | コンポーネント | 説明 |
|---------|------|---------------|------|
| sw.category.detail.base | `/index/:id/base` | sw-category-detail-base | 基本情報タブ |
| sw.category.detail.cms | `/index/:id/cms` | sw-category-detail-cms | CMSレイアウトタブ |
| sw.category.detail.products | `/index/:id/products` | sw-category-detail-products | 商品タブ |
| sw.category.detail.seo | `/index/:id/seo` | sw-category-detail-seo | SEOタブ |
| sw.category.detail.customEntity | `/index/:id/customEntity` | sw-category-detail-custom-entity | カスタムエンティティタブ |

## 入出力項目

### カテゴリ基本情報入力項目

| 項目名 | 項目ID | 入力形式 | 必須 | バリデーション | 説明 |
|--------|--------|----------|------|----------------|------|
| カテゴリ名 | name | テキスト | Yes | 最低1文字以上 | カテゴリの表示名 |
| アクティブ | active | チェックボックス | No | - | カテゴリの有効/無効 |
| タグ | tags | タグ選択 | No | - | 分類用タグ |
| 説明 | description | リッチテキスト | No | - | カテゴリの説明文 |
| カテゴリタイプ | type | セレクト | Yes | - | ページ/フォルダ/リンク |
| CMSレイアウト | cmsPageId | エンティティ選択 | No | - | 表示に使用するCMSページ |

### SEO入力項目

| 項目名 | 項目ID | 入力形式 | 必須 | バリデーション | 説明 |
|--------|--------|----------|------|----------------|------|
| SEO URL | seoUrl | テキスト | No | URL形式 | カスタムSEO URL |
| メタタイトル | metaTitle | テキスト | No | 最大255文字 | ページのtitleタグ |
| メタ説明 | metaDescription | テキストエリア | No | 最大320文字推奨 | ページのmeta description |
| メタキーワード | keywords | テキスト | No | - | meta keywords（非推奨） |

## 表示項目

### カテゴリツリー表示項目

| 項目名 | 表示形式 | データソース | 説明 |
|--------|----------|--------------|------|
| カテゴリ名 | テキスト+アイコン | category.name | カテゴリ名と展開/折りたたみアイコン |
| 子カテゴリ数 | バッジ | category.childCount | 子カテゴリの数 |
| アクティブ状態 | アイコン | category.active | 有効/無効の視覚的表示 |

### カテゴリ詳細表示項目

| 項目名 | 表示形式 | データソース | 説明 |
|--------|----------|--------------|------|
| カテゴリパス | パンくず | 親カテゴリ | ルートからの階層パス |
| プレビュー画像 | サムネイル | category.media | カテゴリの代表画像 |
| 割り当て商品数 | 数値 | product_category | このカテゴリに割り当てられた商品数 |

## イベント仕様

### 1-カテゴリ選択（setCategory）

ツリーでカテゴリを選択した際の処理：

1. 選択されたカテゴリIDを取得
2. カテゴリデータをリポジトリから読み込み
3. 関連データ（seoUrls, tags, media等）を取得
4. CMSページが設定されている場合はCMSページデータを取得
5. カスタムフィールドセットを読み込み
6. 詳細ペインにデータを表示

### 2-カテゴリ保存（onSave）

カテゴリ情報の保存処理：

1. エントリーポイント上書きの確認（必要な場合）
2. SEO URLの更新
3. CMSページIDのデフォルト設定との比較
4. カテゴリリポジトリでの保存実行
5. 成功時：成功フラグを設定、カテゴリを再読み込み
6. 失敗時：エラー通知を表示

### 3-ランディングページ保存（onSaveLandingPage）

ランディングページの保存処理：

1. 販売チャネルの割り当てチェック
2. スロット設定の処理
3. ランディングページリポジトリでの保存実行
4. 新規作成の場合は詳細ページへリダイレクト

### 4-言語切替（onChangeLanguage）

表示言語を変更した際の処理：

1. 新しい言語IDを設定
2. ランディングページまたはカテゴリを再読み込み
3. 翻訳データを反映

### 5-離脱時変更確認（beforeRouteLeave）

ページ離脱前の未保存変更チェック：

1. カテゴリの変更セットを生成
2. 変更がある場合は確認モーダルを表示
3. 保存を選択：保存後に遷移
4. 破棄を選択：変更を破棄して遷移

## データベース更新仕様

### 操作別データベース影響一覧

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| カテゴリ表示 | category | SELECT | カテゴリツリーの取得 |
| カテゴリ詳細表示 | category | SELECT | 単一カテゴリの詳細取得 |
| カテゴリ保存 | category | INSERT/UPDATE | カテゴリ情報の保存 |
| カテゴリ削除 | category | DELETE | カテゴリの削除 |
| SEO URL更新 | seo_url | INSERT/UPDATE | SEO URLの更新 |

### テーブル別更新項目詳細

#### category

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | *, seoUrls, tags, media | ID指定または階層取得 | 関連データ含む |
| INSERT/UPDATE | name, active, visible, type, cms_page_id, media_id, ... | フォーム入力値 | 翻訳含む |
| DELETE | id | 指定ID | 子カテゴリも再帰的に削除 |

#### seo_url

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | seo_path_info, is_canonical | カスタムURL入力値 | 言語別に管理 |

## メッセージ仕様

| メッセージID | 種別 | 表示条件 | メッセージ内容 |
|-------------|------|----------|--------------|
| messageSaveSuccess | 成功 | 保存成功 | カテゴリが保存されました |
| notificationSaveErrorMessage | エラー | 保存失敗 | 保存中にエラーが発生しました |
| notificationSaveErrorMessageRequiredFieldsInvalid | エラー | 必須項目未入力 | 必須項目を入力してください |
| landing_page_sales_channel_blank | エラー | 販売チャネル未設定 | 販売チャネルを設定してください |

## 例外処理

| 例外種別 | 発生条件 | 処理内容 |
|---------|---------|---------|
| カテゴリ読み込みエラー | 存在しないIDまたは権限不足 | エラー通知表示、一覧に戻る |
| 保存時バリデーションエラー | 必須項目未入力 | エラーフィールドをハイライト |
| エントリーポイント上書き | 他のカテゴリが既に設定済み | 確認モーダルを表示 |
| 削除制約エラー | 子カテゴリが存在 | 警告メッセージを表示 |

## 備考

- カテゴリタイプは「ページ」「フォルダ」「リンク」の3種類
- フォルダタイプは商品を持てない（ナビゲーション整理用）
- リンクタイプは外部URLまたは内部ページへのリダイレクト
- エントリーポイント（販売チャネルのルートカテゴリ）は特別な扱い

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

カテゴリエンティティと階層構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | category entity | カテゴリエンティティの定義 | parentId, childCount, path等の階層管理属性 |
| 1-2 | store | `src/Administration/Resources/app/administration/src/module/sw-category/page/sw-category-detail/store/` | カテゴリ状態管理ストア |

**読解のコツ**: Shopwareのカテゴリは`parentId`で親子関係を持ち、`path`に祖先IDのパイプ区切りリストを保持します。

#### Step 2: エントリーポイントを理解する

モジュール登録とルーティング設定を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-category/index.js` | モジュール登録、ルート定義、検索マッチャー |

**主要処理フロー**:
1. **41行目**: `Module.register('sw-category', {...})` でモジュール登録
2. **53-83行目**: `searchMatcher` でカスタム検索結果を定義
3. **85-158行目**: ルート定義（index, detail, landingPageDetail）
4. **199-208行目**: ナビゲーションメニューの定義

#### Step 3: 詳細コンポーネントを理解する

カテゴリ詳細ページのメインロジックを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `src/Administration/Resources/app/administration/src/module/sw-category/page/sw-category-detail/index.js` | 詳細ページのdata、computed、methodsの実装 |

**主要処理フロー**:
- **52-68行目**: `data()` で状態変数を定義
- **94-96行目**: `categoryRepository` でカテゴリリポジトリを取得
- **210-223行目**: `categoryCriteria` でクエリ条件を構築（アソシエーション含む）
- **337-362行目**: `createdComponent()` で初期化処理
- **497-507行目**: `setCategory()` でカテゴリをストアに設定
- **623-667行目**: `onSave()` でカテゴリ保存処理

#### Step 4: ツリーコンポーネントを理解する

カテゴリツリーの実装を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sw-category-tree | `src/Administration/Resources/app/administration/src/module/sw-category/component/sw-category-tree/` | ツリー表示、ドラッグ＆ドロップ、選択処理 |

### プログラム呼び出し階層図

```
sw-category (モジュール)
    |
    +-- sw-category-detail (ページコンポーネント)
           |
           +-- sw-category-tree (左ペイン：ツリー)
           |      +-- カテゴリ選択 → setCategory()
           |      +-- ドラッグ＆ドロップ → moveCategory()
           |
           +-- sw-category-view / sw-landing-page-view (右ペイン)
                  |
                  +-- sw-category-detail-base (基本情報タブ)
                  +-- sw-category-detail-cms (CMSタブ)
                  +-- sw-category-detail-products (商品タブ)
                  +-- sw-category-detail-seo (SEOタブ)
                  |
                  +-- onSave() → categoryRepository.save()
                  +-- onSaveLandingPage() → landingPageRepository.save()
```

### データフロー図

```
[入力]                    [処理]                         [出力]

ツリーでカテゴリ選択 ───▶  sw-category-detail         ───▶   詳細フォーム表示
                          ・setCategory()
                          ・getAssignedCmsPage()

フォーム編集入力   ───▶    onSave()                   ───▶   保存結果通知
                          ・updateSeoUrls()                  ツリー更新
                          ・categoryRepository.save()

ドラッグ＆ドロップ ───▶    sw-category-tree           ───▶   階層構造更新
                          ・moveCategory()
                          ・updateAfterDrop()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `src/Administration/Resources/app/administration/src/module/sw-category/index.js` | ソース | モジュール登録・ルーティング定義 |
| index.js | `src/Administration/Resources/app/administration/src/module/sw-category/page/sw-category-detail/index.js` | ソース | 詳細ページのメインロジック |
| store/ | `src/Administration/Resources/app/administration/src/module/sw-category/page/sw-category-detail/store/` | ソース | カテゴリ状態管理ストア |
| sw-category-tree | `src/Administration/Resources/app/administration/src/module/sw-category/component/sw-category-tree/` | ソース | カテゴリツリーコンポーネント |
| sw-category-detail-base | `src/Administration/Resources/app/administration/src/module/sw-category/view/sw-category-detail-base/` | ソース | 基本情報タブ |
| sw-category-detail-cms | `src/Administration/Resources/app/administration/src/module/sw-category/view/sw-category-detail-cms/` | ソース | CMSレイアウトタブ |
| sw-category-detail-products | `src/Administration/Resources/app/administration/src/module/sw-category/view/sw-category-detail-products/` | ソース | 商品タブ |
| sw-category-detail-seo | `src/Administration/Resources/app/administration/src/module/sw-category/view/sw-category-detail-seo/` | ソース | SEOタブ |
| acl/index.js | `src/Administration/Resources/app/administration/src/module/sw-category/acl/index.js` | ソース | 権限マッピング定義 |
