# 機能設計書 47-コメント編集

## 概要

本ドキュメントは、自分のコメントを編集する機能の設計を定義するものである。

### 本機能の処理概要

**業務上の目的・背景**：コメント投稿後に誤字脱字の修正や、内容の補足・訂正が必要になることがある。コメント編集機能は、自分が投稿したコメントの本文を後から修正できるようにすることで、より正確で適切なコミュニケーションを支援する。ただし、他のユーザーが投稿したコメントは編集できないよう制限されている。

**機能の利用シーン**：
- 投稿したコメントに誤字脱字があったとき
- コメント内容を補足・訂正したいとき
- より適切な表現に修正したいとき

**主要な処理内容**：
1. PATCHリクエストでコメント本文の更新内容を受け取る
2. 対象カードおよびコメントへのアクセス権限を確認
3. コメント作成者と現在のユーザーが一致することを確認
4. コメント本文を更新
5. Turbo StreamまたはJSONレスポンスで結果を返却

**関連システム・外部連携**：特になし（内部処理のみ）

**権限による制御**：
- カードへのアクセス権限を持つユーザーのみ操作可能
- コメントの作成者（creator）のみが編集可能（ensure_creatorship）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 31 | カードコメント編集画面 | 主機能 | 自分のコメントを編集 |
| 19 | カード詳細画面 | 参照画面 | 編集ボタンからコメント編集画面へ遷移 |

## 機能種別

CRUD操作（Update）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| card_id | Integer | Yes | 対象カードのID（URLパラメータ） | 存在確認、アクセス権限確認 |
| id | UUID | Yes | 対象コメントのID（URLパラメータ） | 存在確認 |
| comment[body] | RichText | Yes | コメント本文（リッチテキスト） | - |

### 入力データソース

- URLパラメータからカードIDとコメントID
- フォームデータまたはJSONからコメント本文

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @comment | Comment | 更新されたコメントオブジェクト |

### 出力先

- Turbo Stream: 画面の動的更新（既存要素の置換）
- JSON: API利用時のレスポンス（204 No Content）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ PATCH /cards/:card_id/comments/:id へのリクエストを受信

2. 認証・認可確認
   └─ CardScopedモジュールのset_cardでカードアクセス権限を確認

3. コメント取得
   └─ set_commentでカードに紐づくコメントを取得

4. 作成者確認
   └─ ensure_creatorshipでコメント作成者と現在のユーザーが一致するか確認

5. コメント更新
   └─ @comment.update!(comment_params) でコメントを更新

6. レスポンス生成
   ├─ Turbo Stream: update.turbo_stream.erb をレンダリング
   └─ JSON: 204 No Content を返却
```

### フローチャート

```mermaid
flowchart TD
    A[PATCH /cards/:card_id/comments/:id] --> B{認証済み?}
    B -->|No| C[401 Unauthorized]
    B -->|Yes| D[set_card: カード取得]
    D --> E{カードアクセス可能?}
    E -->|No| F[404 Not Found]
    E -->|Yes| G[set_comment: コメント取得]
    G --> H{コメント存在?}
    H -->|No| I[404 Not Found]
    H -->|Yes| J{作成者確認}
    J -->|No| K[403 Forbidden]
    J -->|Yes| L[comment_paramsでパラメータ抽出]
    L --> M[@comment.update!でコメント更新]
    M --> N{リクエスト形式}
    N -->|Turbo Stream| O[update.turbo_stream.erb]
    N -->|JSON| P[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-47-01 | 作成者制限 | コメント作成者のみが編集可能 | 常時 |
| BR-47-02 | カードスコープ | カードに紐づくコメントのみ更新可能 | 常時 |
| BR-47-03 | カード更新日時 | コメント更新時にカードのupdated_atが更新される | touch: trueにより |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | 対象カードの存在確認とアクセス権限確認 |
| コメント取得 | comments | SELECT | 対象コメントの存在確認 |
| コメント更新 | comments | UPDATE | コメントのupdated_atを更新 |
| リッチテキスト更新 | action_text_rich_texts | UPDATE | コメント本文を更新 |
| カード更新 | cards | UPDATE | touch: trueによりupdated_atを更新 |

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

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | WHERE card_id = @card.id AND id = params[:id] | コメント取得 |
| UPDATE | updated_at | 現在時刻 | 自動設定 |

#### action_text_rich_texts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | body | params[:comment][:body] | コメント本文 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未認証状態でアクセス | ログイン画面へリダイレクト |
| 403 | Forbidden | コメント作成者以外が編集しようとした | エラーメッセージ表示 |
| 404 | Not Found | カードまたはコメントが存在しない/アクセス権限なし | エラーメッセージ表示 |
| 422 | Unprocessable Entity | バリデーションエラー | エラーメッセージ表示 |

### リトライ仕様

特になし

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

- `update!` メソッドにより、コメント更新とカードのtouch更新が同一トランザクション内で実行される
- 失敗時は自動的にロールバックされる

## パフォーマンス要件

- コメント更新のレスポンス時間: 500ms以内

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

- 認証必須: ログイン済みユーザーのみアクセス可能
- 認可:
  - CardScopedモジュールにより、アクセス可能なカードのコメントのみ操作可能
  - ensure_creatorshipにより、コメント作成者のみ編集可能
- Strong Parameters: `params.expect(comment: [ :body, :created_at ])` でパラメータをホワイトリスト制御
- XSS対策: Action Textによるサニタイズ

## 備考

- 編集画面からは削除操作も可能（削除ボタンが配置されている）
- キーボードショートカット: Ctrl+Enter/Cmd+Enterで保存、ESCでキャンセル

---

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

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

### 推奨読解順序

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

まず、コメントのデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | comment.rb | `app/models/comment.rb` | Commentモデルの定義、creatorとの関連 |
| 1-2 | schema.rb | `db/schema.rb` | commentsテーブルの構造（252-260行目） |

**読解のコツ**:
- `belongs_to :creator, class_name: "User"` でコメント作成者を参照
- `belongs_to :card, touch: true` でコメント更新時にカードも更新

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

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | comments_controller.rb | `app/controllers/cards/comments_controller.rb` | updateアクションの実装（27-34行目） |
| 2-2 | card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | カードスコープの共通処理 |

**主要処理フロー**:
1. **4-5行目**: `before_action :set_comment` と `before_action :ensure_creatorship` でコメント取得と作成者確認
2. **27-34行目**: `update` アクションでコメント更新
3. **28行目**: `@comment.update! comment_params` で更新実行
4. **50-52行目**: `ensure_creatorship` で作成者確認

#### Step 3: ビューレンダリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit.html.erb | `app/views/cards/comments/edit.html.erb` | コメント編集フォーム |
| 3-2 | update.turbo_stream.erb | `app/views/cards/comments/update.turbo_stream.erb` | Turbo Streamレスポンス |
| 3-3 | _comment.html.erb | `app/views/cards/comments/_comment.html.erb` | コメント部分テンプレート |

**主要処理フロー**:
- **edit.html.erb 9-28行目**: 編集フォーム、Ctrl+Enter/ESCキーに対応
- **edit.html.erb 22-26行目**: 削除ボタン（確認ダイアログ付き）
- **update.turbo_stream.erb 1行目**: `turbo_stream.replace` でコメントを置換

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

```
PATCH /cards/:card_id/comments/:id
    │
    ├─ CardScoped#set_card
    │      └─ Current.user.accessible_cards.find_by!(number: params[:card_id])
    │
    ├─ Cards::CommentsController#set_comment
    │      └─ @comment = @card.comments.find(params[:id])
    │
    ├─ Cards::CommentsController#ensure_creatorship
    │      └─ head :forbidden if Current.user != @comment.creator
    │
    └─ Cards::CommentsController#update
           │
           ├─ @comment.update!(comment_params)
           │      └─ Comment.update!
           │             └─ belongs_to :card, touch: true
           │
           └─ respond_to
                  ├─ format.turbo_stream
                  │      └─ update.turbo_stream.erb
                  │             └─ turbo_stream.replace
                  │
                  └─ format.json
                         └─ head :no_content (204)
```

### データフロー図

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

PATCH リクエスト ──────▶ Cards::CommentsController#update
  ├─ card_id                  │
  ├─ id (comment_id)          ├─▶ set_card + set_comment
  └─ comment[body]            │      └─▶ @card, @comment
                              │
                              ├─▶ ensure_creatorship
                              │      └─▶ Current.user == @comment.creator?
                              │
                              ├─▶ comment_params
                              │      └─▶ { body: }
                              │
                              └─▶ @comment.update!
                                     │
                                     ▼
                                  DB更新
                                  (comments, action_text_rich_texts, cards touch)
                                     │
                                     ▼
                          ┌──────────┴──────────┐
                          │                     │
                   Turbo Stream              JSON
                   (turbo_stream.replace)  (204 No Content)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| comments_controller.rb | `app/controllers/cards/comments_controller.rb` | コントローラー | コメントCRUD処理 |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | Concern | カードスコープ共通処理 |
| comment.rb | `app/models/comment.rb` | モデル | コメントのデータモデル定義 |
| edit.html.erb | `app/views/cards/comments/edit.html.erb` | ビュー | コメント編集フォーム |
| update.turbo_stream.erb | `app/views/cards/comments/update.turbo_stream.erb` | ビュー | Turbo Streamレスポンス |
| _comment.html.erb | `app/views/cards/comments/_comment.html.erb` | ビュー | コメント部分テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | スキーマ | commentsテーブル定義 |
