# 機能設計書 14-カラム編集

## 概要

本ドキュメントは、カラムの名前や色を編集する機能の設計を記載する。

### 本機能の処理概要

本機能は、既存のカラム（列）の名前や表示色を変更する機能である。カラムの属性を更新することで、ボードのワークフローを柔軟に調整できる。

**業務上の目的・背景**：プロジェクトの進行に伴い、カラムの名前を変更したり、視覚的な区別のために色を変更したりするニーズがある。例えば、「In Progress」を「開発中」に日本語化したり、緊急度の高いカラムを赤色に変更したりすることで、チームの作業効率を向上させる。

**機能の利用シーン**：ボードカラム詳細画面またはボード設定画面から、カラムの編集フォームにアクセスし、名前や色を変更して保存する。変更は即座に画面に反映される。

**主要な処理内容**：
1. カラム名と色のパラメータを受け取る
2. カラムレコードを更新
3. 関連カードにtouch_allを実行してキャッシュを無効化
4. Turbo StreamsでUIを動的に更新

**関連システム・外部連携**：Turbo Streamsを使用してリアルタイムUI更新を行う。morphメソッドによりDOMの差分更新を実現。JSONレスポンスにも対応。

**権限による制御**：ユーザーがアクセス可能なボードに属するカラムのみ編集可能。BoardScopedによりボードへのアクセス権を検証。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | ボードカラム詳細画面 | 主画面 | カラムの名前や色を編集 |
| 12 | ボード設定画面 | 参照画面 | ボード設定からカラム編集へ遷移 |

## 機能種別

CRUD操作（Update）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| board_id | UUID | Yes | 対象ボードのID | 存在するボードであること、ユーザーがアクセス可能であること |
| id | UUID | Yes | 対象カラムのID | 該当ボードに属するカラムであること |
| column[name] | String | No | 新しいカラム名 | 最大255文字 |
| column[color] | String | No | 新しいカラムの色 | Color::COLORSに含まれる値 |

### 入力データソース

- URLパラメータ: board_id、id（カラムID）
- リクエストボディ: column[name]、column[color]
- セッション: 認証済みユーザー情報（Current.user）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| column.id | UUID | 更新されたカラムのID |
| column.name | String | 更新後のカラム名 |
| column.color | String | 更新後のカラムの色 |
| column.updated_at | DateTime | 更新日時 |

### 出力先

- データベース: columnsテーブル、cardsテーブル（touch_all）
- 画面表示: Turbo Streamsによる動的更新
- JSONレスポンス: 204 No Content（API経由の場合）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（PATCH/PUT /boards/:board_id/columns/:id）
   └─ BoardScopedによりboard_idからボードを取得
   └─ set_columnによりidからカラムを取得

2. パラメータ取得
   └─ column_params: name, color

3. カラム更新
   └─ @column.update!(column_params)

4. after_save_commit: 関連カードのtouch_all
   └─ saved_change_to_name? || saved_change_to_color? の場合に実行

5. レスポンス生成
   └─ Turbo Streams: カラムUIをmorphで更新
   └─ JSON: 204 No Content
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[BoardScoped: ボード取得]
    B --> C[set_column: カラム取得]
    C --> D[column_paramsを抽出]
    D --> E[@column.update!]
    E --> F{バリデーション}
    F -->|失敗| G[422 Unprocessable Entity]
    F -->|成功| H[UPDATE実行]
    H --> I{name or colorが変更?}
    I -->|Yes| J[cards.touch_all]
    I -->|No| K[スキップ]
    J --> L{レスポンス形式}
    K --> L
    L -->|Turbo Streams| M[UIをmorph更新]
    L -->|JSON| N[204 No Content]
    M --> O[終了]
    N --> O
    G --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | カード連動更新 | カラム名または色が変更されると関連カードのupdated_atが更新される | name/color変更時 |
| BR-02 | ボードタッチ | カラム更新時に親ボードのupdated_atが更新される（touch: true） | カラム更新時 |
| BR-03 | 部分更新許可 | name/colorの一方のみの更新も許可 | 常時 |

### 計算ロジック

該当なし

## データベース操作仕様

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カラム更新 | columns | UPDATE | name/color/updated_atを更新 |
| カード更新 | cards | UPDATE | 関連カードのupdated_atを一括更新 |
| ボード更新 | boards | UPDATE | 親ボードのupdated_atを更新 |

### テーブル別操作詳細

#### columns

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | column_params[:name] | 指定時のみ更新 |
| UPDATE | color | column_params[:color] | 指定時のみ更新 |
| UPDATE | updated_at | 現在時刻 | 自動更新 |

#### cards（after_save_commit）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | updated_at | 現在時刻 | touch_allで一括更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | 存在しないboard_id/column_idが指定 | RecordNotFound例外 |
| 422 | Unprocessable Entity | バリデーションエラー | エラーメッセージ表示 |
| 401 | Unauthorized | 未認証ユーザー | ログイン画面へリダイレクト |

### リトライ仕様

該当なし（失敗時はエラーを返す）

## トランザクション仕様

- update!メソッドによる暗黙のトランザクション
- after_save_commitは親トランザクションのコミット後に実行

## パフォーマンス要件

- レスポンス時間: 200ms以内
- カードtouch_all: 大量のカードがある場合は影響大（バルクUPDATE）

## セキュリティ考慮事項

- 認証: ログイン済みユーザーのみアクセス可能
- 認可: BoardScopedによりCurrent.user.boardsでアクセス可能なボードのみ
- パラメータ: Strong Parameters（column_params）で許可パラメータを制限

## 備考

- morph更新により、入力フォーカスを保持したままUIを更新可能
- touch_allによりETags/キャッシュの自動無効化が行われる

---

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

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

### 推奨読解順序

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

カラム更新時の連動処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | column.rb | `app/models/column.rb` | after_save_commitでカードをtouch_all |

**読解のコツ**: after_save_commitはトランザクションコミット後に実行されるため、ロールバック時には実行されない。saved_change_to_name?などのメソッドでどのカラムが変更されたかを検出できる。

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

リクエストを受け付けるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | columns_controller.rb | `app/controllers/boards/columns_controller.rb` | updateアクションの実装 |

**主要処理フロー**:
1. **4行目**: before_action :set_columnでカラムを取得
2. **25-32行目**: updateアクションで@column.update!を実行
3. **28行目**: Turbo Streams形式のレスポンス
4. **29行目**: JSON形式では204 No Contentを返す

#### Step 3: コールバック処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | column.rb | `app/models/column.rb` | after_save_commitコールバック（8行目） |

**主要処理フロー**:
- **8行目**: saved_change_to_name? || saved_change_to_color?の場合にcards.touch_allを実行

#### Step 4: ビュー更新を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update.turbo_stream.erb | `app/views/boards/columns/update.turbo_stream.erb` | Turbo Streamsでカラムをmorph更新 |

**主要処理フロー**:
- **1行目**: turbo_stream.replaceでmethod: :morphを使用し差分更新

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

```
Boards::ColumnsController#update
    │
    ├─ BoardScoped (concern)
    │      └─ set_board (Current.user.boards.find)
    │
    ├─ set_column
    │      └─ @board.columns.find(params[:id])
    │
    ├─ column_params
    │      └─ params.expect(column: [:name, :color])
    │
    └─ @column.update!
           │
           ├─ ActiveRecord UPDATE
           │
           └─ after_save_commit (conditional)
                  └─ cards.touch_all
```

### データフロー図

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

PATCH /boards/:board_id/columns/:id
  └─ column[name]
  └─ column[color]
         │
         ▼
┌─────────────────┐
│ board/column取得 │ ───▶ Current.user.boards.find
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ UPDATE columns  │ ───▶ name, color更新
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ after_save_commit│ ───▶ cards.touch_all (条件付き)
└─────────────────┘
         │
         ▼
┌─────────────────┐
│ Turbo Streams   │ ───▶ morph更新
└─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| columns_controller.rb | `app/controllers/boards/columns_controller.rb` | コントローラー | カラム更新リクエストの処理 |
| board_scoped.rb | `app/controllers/concerns/board_scoped.rb` | concern | ボード取得 |
| column.rb | `app/models/column.rb` | モデル | カラムモデル、after_save_commitコールバック |
| update.turbo_stream.erb | `app/views/boards/columns/update.turbo_stream.erb` | ビュー | 更新後のUI更新（morph） |
| schema.rb | `db/schema.rb` | スキーマ | columnsテーブル定義（239-249行目） |
