Railsでコメントに対する返信機能を実装した！



#実装内容
今回はコメントに対する返信機能の作成です
- 返信テーブルはコメントテーブルと同じです。
- コメントの返信フォームからコメントを送信すると、そのコメントに対しての返信になる
- コメントTableにreply_commentを追加し、そこに親コメントのidを入れる

#実装コード

コメントテーブルに返信用カラムを作ります。
ここには、親コメントidを格納される予定です。

```
 rails g migration AddReply_user_idToComment reply_comment:int
```

##モデル

```ruby:model/product.rb
has_many :comments
has_many :replies, class_name: Comment, foreign_key: :reply_comment, dependent: :destroy
```

```ruby:model/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :product
end
```

外部キーにreply_commentを登録。
いまいちprimary_keyとforeign_keyあたりの使い方は理解できてません。


##コントローラ

ツイート詳細ページにてコメントできるようにします。

```ruby:controller/comments_controller.rb
class CommentsController < ApplicationController
  #コメント削除アクション
  def destroy
    @comment = Comment.find(params[:id]).destroy
    redirect_to "/products/#{@comment.product.id}"
  end

  #コメント追加アクション
  def create
   @comment = Comment.create(comment_params)
   # @product = Comment.find_by(product_id: params[:product_id])
    redirect_to "/products/#{@comment.product.id}"
  end

  private
  #ストロングパラメータ
  #返信機能のためにreply_commentを追加
  def comment_params
    comment_params = params.require(:comment).permit(:content, :image,:reply_comment).merge(user_id: current_user.id, product_id: params[:product_id])
  end
end

```
```ruby:controller/products_controller.rb
def show
    @product = Product.find(params[:id])

    #指定のツイートのコメントを列挙
    @comments = Comment.includes(:user).where(product_id: @product.id)

    #いいね機能
    @like = Like.find_by(user_id: current_user.id)
    @likes = Like.new(user_id: current_user.id, product_id: params[:product_id])

    #コメント追加
    @new_comments = Comment.new
  end
```


アソシエーションを用いるとcomments.userを使えるが、eachメソッドの内部で用いるとN+1問題が発生するので、includesメソッドで先にuserテーブルの情報も取得しておく。

気になったこと
先にuserテーブルの情報を取得すると書いてあったが、これってどこに格納されるんだ・・・

##View

```html:products/show.html.erb
#コメントの一覧
<% @comments.each do |comment| %>
   <li class="collection-item">
       <div>
　　　　　　　　　　　　　　　　　　#ユーザー情報カードの部分テンプレートへ
         <%= render 'products/user' , user: comment.user %><br>
　　　　　　　　　　　　　　　　　　　#親コメントが存在する場合、出力
        　　　<% unless comment.reply_comment == nil %>
            コメント先-----------<br>
　　　　　　　　　　　　　　　　　　　　　　　　#親コメントのユーザーを出力
            <%= render 'products/user' , user: @comments.find(comment.reply_comment).user %><br>
            #親コメントの内容を出力
            <%= @comments.find(comment.reply_comment).content %><br>
            ----------------------<br>
          <% end %>
　　　　　　　　　　　　　　　　　　　　#コメントを出力
          <%= comment.content %>
　　　　　　　　　　　　　　　　　　　#　このコメントに対して投稿
          <%= form_for [@product, @new_comments] do |f| %>
           <ul class="collapsible">
              <li>
              <div class="collapsible-header"><i class="material-icons">message</i>コメントに返信をする</div>
               <div class="collapsible-body">
               <%= f.text_field :content %>
               #ここ大事！hidden_fieldでフォーム無しに送信先コメントのidをreply_commentに追加
               <%= f.hidden_field :reply_comment, :value => comment.id  %>
               <%= button_tag type:"submit", class: "waves-effect waves-light btn" do %>
               <%= f.submit "投稿する" %>
          <% end %>
         </div>
         </li>
        </ul>
      <% end %>
     </div>
  #コメント削除機能
  <% if comment.user_id == current_user.id %>
    <%= link_to product_comment_path(@product,comment), method: :delete , class: "waves-effect waves-light btn" do %>
    <i class="fa fa-arrow-left"></i>
    <span>コメント削除</span>
    <% end %>
  <% end %>
 </li>
<% end %>
```

```
<%= f.hidden_field :reply_comment, :value => comment.id  %>
```
こんな使い方、あるんですね。

#結果
<img width="658" alt="スクリーンショット 2018-05-11 21.28.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/253129/8bb9a551-2f1c-6e3e-acc4-179f802827e5.png">


<img width="606" alt="スクリーンショット 2018-05-11 22.40.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/253129/514e8dfa-5dae-14b0-0903-9d17095d6ba1.png">

#終わり
もう少し、動くだけでなくクエリや保守性を重要視できるコードを書きたいなあと思いました。
マサカリがあればお願いします。おそらく綺麗なコードではないと思いますので。