Rails コメント機能の実装

# はじめに
今回はXクローンの実装にてコメント機能を実装したので、自分自身の備忘録として、また今後コメント機能を実装する方のお役に立てればと思いこちらの記事を書くことにしました。


# コメント機能の作成
要件として、投稿されたツイートに対して、コメントをつけることができる機能を実装する。

※ 前提として、ユーザー登録、投稿機能などがすでに実装されているものとします。


# コメント機能実装に必要なファイル
#### 作成するモデル・テーブル
- commentモデル
- commentテーブル

#### 作成するコントローラー
- commentコントローラー


# commentテーブルの作成
今回コメント機能を実装するにあたり、必要なテーブルはこちらのようになります。

| カラム名 　　　　| データ型 | 　カラムの説明　　　|
|:-----------|------------:|:------------:|
|id              |bigint| コメントのID         |
|user_id         |bigint| ユーザーのID       |
|post_id         |bigint| 投稿のID         |
|comment_content |text  | コメント本文           |


# commentモデルの作成
```
rails g model Comment
```

```ruby:create_comment.rb
class CreateComments < ActiveRecord::Migration[7.0]
  def change
    create_table :comments do |t|
      t.text :comment_content
      t.references :user, foreign_key: true
      t.references :post, foreign_key: true

      t.timestamps
    end
  end
end
```

```
rails db:migrate
```


# 関連付けの設定
commentモデルは、user_idとpost_idと関連付けする必要がある。

userモデル／postモデル
1人のユーザーが複数の画像を投稿できる、1対Nの関係

postモデル／commentモデル
1つの投稿画像に対して、複数のコメントを投稿できる、1対Nの関係

userモデル／commentモデル
1人のユーザーが複数のコメントを投稿できる、1対Nの関係

![スクリーンショット 2024-02-28 23.08.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335067/96365d6f-30dc-06ae-1b3b-6316d0c06570.png)


```ruby: user.rb
has_many :comments, dependent: :destroy
```

```ruby: post.rb
has_many :comments, dependent: :destroy
```

```ruby: comment.rb
belongs_to :user
belongs_to :post
```

# ルーティングの設定
```ruby: routes.rb
resources :posts do
  resources :comments, only: %i[create]
end
```
コメントは、ポストに対してコメントされるので、ネストしてあげる。
ルーティングを見てみると
![スクリーンショット 2024-02-29 23.26.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3335067/485ac02e-cb7f-925d-94b2-58316834a715.png)
こんな感じでルーティングが作成される。


# controllerの作成

```
rails g controller comments
```

```ruby: comments_controller.rb
class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    #@postに関連付けられたCommentモデルの新しいインスタンス（オブジェクト）を作成
    @comment = @post.comments.build(comment_params)
    #コメントの所有者を現在ログインしているユーザーに設定する
    @comment.user = current_user

    @comment.save
    redirect_to request.referer
  end

  private

  def comment_params
    params.require(:comment).permit(:comment_content, :comment_img)
  end
end
```

#### コメントを投稿するためのインスタンス変数を定義
```ruby: post_controller.rb
def show
    @post = Post.find_by(id: params[:id])
    @comments = @post.comments.includes(:user).order(created_at: :desc).page(params[:page]).per(3)
    #ここを追加
    @comment = current_user.comments.new
  end
```

# viewの設定
ポストの詳細画面でコメントを投稿できるようにする。

```ruby: posts/show.html.slim
= form_with model: [@post, @comment], url: post_comments_path(@post), method: :post do |f|
      .comment_form
        .form_wrapper
          .form_top.d-flex
            - if current_user.icon.attached?
              = link_to user_path(current_user) do 
                = image_tag current_user.icon, class:"icon_img mt-4 comment_user_icon"
            - else 
              = link_to user_path(current_user) do 
                img.home_img[src="/assets/icon.png"]
            
            = f.text_area :comment_content, placeholder: '返信をツイート', rows: 3, cols: 80, class: 'form-control mt-4'

          .form_buttons
            .form_button_set 
              .form_option
                = f.file_field :comment_img, class: 'form-control visually-hidden', accept: 'image/*', id: 'file-input'
                label[for="file-input"] 
                  i.fa-regular.fa-image
              .comment_submit
                = f.submit "投稿", class: "btn"
```

form_with部分で、`[@post,@comment]`のように、配列でインスタンス変数を２つ指定しているところに注意する。
コメントは、postに結びついたcomenntであるので、2つ指定する必要がある。
commentをcreateするためのリクエストは、ルーティングでもネストしているため、2つの情報を入れる必要がある。



これで、投稿ができるようになっていると思います。

# 最後に
今回はコメントの投稿機能の実装しかしていないので、今後削除機能の追加もしていきたいと考えています。

その時には、また追記で記事を書いて行こうと思います。

最後まで読んでいただきありがとうございました。