【Rails】コメント機能の実装

### はじめに
ブログっぽいWebサービスにコメント機能を追加してみました。
自分のメモ用、そして同じような機能を実装しようとしている初学者の方の参考になれば幸いです！

## 前提
Ruby : 2.6.4
Rails : 5.2.3

ユーザー認証にdeviseを使用しています。

#Comment Model作成

UserモデルのユーザーがPostモデルに対してコメントをできるようにします。
実装方法としては、フォロー機能のように中間テーブルを用意し、その中間テーブル（Comment Model）のcontentカラムに
実際のコメントを追加します。

```
$ rails g model comment content:string user:references post:references
$ rails db:migrate
```

``` file:user.rb
  has_many :comments
```

```file:post.rb
  has_many :comments
```

```file:comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post
end
```
comment.rbについてですが、自分の手動でのミスにより、post_idとの関連付けがうまくいっていない状態で
コメントのテストを行なってしまい、外部キーがnilになってしまいエラーになってしまいました。
この問題の解決は、

```file:comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post, optional: true
end
```

このように、optional: true を追記することで（一時的に）解決できます。
ここで、自動テストのありがたみに気づきました（笑）
外部キーのnilを許可するパターンはあまりない気がするので、使うことはそこまでないのかなと思います。

### コントローラー作成
comments_controller.rbを作成

```
$ rails g controller comments
```

```file:comments_controller.rb
class CommentsController < ApplicationController
  before_action :authenticate_user!

  def create
    post = Post.find(params[:post_id])
    @comment = post.comments.build(comment_params)
    @comment.user_id = current_user.id
    if @comment.save
      flash[:success] = "コメントしました"
      redirect_back(fallback_location: root_path)
    else
      flash[:success] = "コメントできませんでした"
      redirect_back(fallback_location: root_path)
    end
  end

  private

    def comment_params
      params.require(:comment).permit(:content)
    end
end

```

createアクションについて。
 @comment.user_id = current_user.idを書いている理由として、
上の２行だけではコメントをしたユーザーの関連付けをできないため書いています。


### ルーティング

```file:routes.rb
  resources :posts do
    resources :comments, only: [:create]
  end
```

### Views

自分の場合はPostのshow.html.erbにコメント一覧とフォームを追加したかった為ここに追加します。

```file:app/views/posts.show.html.erb
<div class="comment-wrapper border-top　mb-10">
  <p class="mt-5">コメント一覧</p>
  <% @comments.each do |c| %>
    <div>
      <% unless c.user.blank? %>
        <a href="<%= user_path(c.user_id) %>"><%= image_tag c.user.image.to_s,
          class:"rounded-circle icon_image mr-3 mb-3"%></a>
        <% end %>
      <%= c.user.username unless c.user.blank? %>
      <br />
      <%= c.content %>
    </div>
    <br />
  <% end %>
  <% if user_signed_in? %>
    <%= form_with(model: [@post, @comment], local: true) do |f| %>
      <%= f.text_area :content, class: "form-control", rows: 5 %>
      <%= button_tag type: "submit", class: "btn btn-success float-right mt-1" do %>
        <i class="far fa-comments"></i> コメントする
      <% end %>
    <% end %>
  <% end %>
</div>
```

特に難しいことはしていませんが、form_with(model: [@post, @comment], local: true) do |f|は、
commentがpostに紐づいている為配列にします。

```file:posts_controller.rb
  def show
    @post = Post.find(params[:id])
    @comments = @post.comments
    @comment = @post.comments.build
  end
```

show.html.erb内で使用している@commentと@commentsは上記のようになります。

### 完成

ログインしていない時

![スクリーンショット 2019-09-14 23.13.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481205/703b6194-caeb-c466-c46b-c313bde1946f.png)


ログイン時

![スクリーンショット 2019-09-14 23.14.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481205/43267191-4926-3940-d2ae-ff4b3d38dc8e.png)


### 終わりに
コメント削除機能をつけ忘れていたので実装します。
何か間違っている点などありましたらコメントやツイッターでお願いします！