【Rails】コメントの編集, 削除機能実装(form_with)

# はじめに
記事投稿アプリの記事に対して、コメントを作れる機能を実装した。
さらに、編集と削除ができるようにしたい。

**完成図**
![完成図.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2684787/617104a7-bfca-fd77-79d2-521bd15ec3a7.gif)


# 前提
下記は作成済み
投稿機能
ユーザーテーブル
コメントテーブル
コメントコントローラー createアクション

下記テーブルは作成済み。
| No |    項目     |   内容    |
|---:|-------------|------------|
| 1  |      OS       |   Mac     |
| 2  |       Ruby      |   2.6.3   |
| 3  |       rails      |      6.0.4 |

# ポイント

・コメント投稿機能とeditテンプレートを同じパーシャルを適用することで、dryにする。
・post_controllerに@commentを追加した。
post/show.html.erbテンプレートからコメントをする際に、
/posts/:post_id/comments/:id/edit　にアクセスする時に使用するcommentモデルの主idを取得するため。
・コメントを編集、削除機能のパーシャルを作成する。
>ログインユーザーがコメント作成者なら編集と削除を実行可能。
ログインユーザーが投稿作成者なら削除のみ実行可能。

# routesの設定
```routes.rb
  resources :posts do
    resources :comments, only: [:create, :edit, :update, :destroy]
  end
```
ターミナルで確認する。
```ターミナル
post_comments　　　　　　　　 POST   /posts/:post_id/comments(.:format) comments#create
edit_post_comment GET    /posts/:post_id/comments/:id/edit(.:format) comments#edit
post_comment　　　　　　　　　　 PATCH  /posts/:post_id/comments/:id(.:format) comments#update
                  PUT    /posts/:post_id/comments/:id(.:format)　　comments#update
                  DELETE /posts/:post_id/comments/:id(.:format)　　comments#destroy
```
# コントローラーの設定
@commentを追加した。
post/show.html.erbテンプレートからコメントをする際に、
/posts/:post_id/comments/:id/edit　にアクセスする時に使用するcommentモデルの主idを取得するため。
```posts_controller.rb
  def show
    @post = Post.find(params[:id])
    @comment = Comment.new # 追加
    @comments = @post.comments
  end
```


```comments_controller.rb
  def edit
    @post = Post.find(params[:post_id])
    @comment = Comment.find(params[:id])
  end

  def update
    @post = Post.find(params[:post_id])
    @comment = Comment.find(params[:id])
    if @comment.update(comment_params)
      flash[:success] = "Comment updated"
      redirect_to @post
    else
      flash[:danger] = "Comment failed"
      render 'edit'
    end
  end

  def destroy
    @post = Post.find(params[:post_id])
    @comment = current_user.comments.find_by(post_id: @post.id)
    @comment.destroy
    redirect_to post_path(params[:post_id])
  end

  private
    def comment_params
      params.require(:comment).permit(:comment_content).merge(post_id: params[:post_id])
      #form tagにてpost_idパラメータを送信して、コメントへpost_idを格納するようにする必要がある。URLを作るために。
    end
```
# コメント一覧表示用にcomment_usersメソッドを作る
```post.rb
  has_many :comments, dependent: :destroy
  has_many :comment_users, through: :comments, source: :user
```

コメント投稿機能とeditテンプレートを同じパーシャルを適用することで、dryにする。
また、コメント一覧を作成し、
その中に、ユーザー情報パーシャル、コメント編集、削除機能パーシャルを作る。
```show.html.erb
<%# コメント機能 %>
<p class="margin-top">コメントする</p>
  <%= render 'comments/comment_form' %>　# edit.html.erbと共通のパーシャル

<p class="margin-top">コメント一覧</p>
<% @comments.each do |comment| %>
  <div class=comments>
    <%= render 'shared/user_small_info', c: comment %>
    <div class="comment_content"> <%= comment.comment_content %> </div>
    <%= render 'comments/comment_edit', post: @post, c: comment %>
  </div>
<% end %>
```
# コメント一覧を作る
ユーザー情報のパーシャルを作成する。
```app/views/shared/_user_small_info.html.erb
<%= link_to gravatar_for(c.user, size: 30), c.user %>
<span class="user">
  <%= c.user.name %>
</span>
<span class="timestamp">
  | Posted <%= time_ago_in_words(c.created_at) %> ago.
</span>
```
コメントを編集、削除機能のパーシャルを作成する。
・ログインユーザーがコメント作成者なら編集と削除を実行可能。
・ログインユーザーが投稿作成者なら削除のみ実行可能。
```comments/_comment_edit.html.erb
<%# comment delete or edit %>
    <% if current_user?(c.user) %>
      <div class="link_to">
        <%= link_to "delete", post_comment_path(post, c), method: :delete, data: { confirm: "You sure?" } %>
          <!--comment => comment_path-->
      </div>
      <div class="link_to">
        <%= link_to "edit", edit_post_comment_path(post, c) %>
      </div>
    <%# login user が投稿者だった場合、削除のみ可能 %>
    <% elsif current_user.id == post.user_id %>
      <div class="link_to">
        <%= link_to "delete", post_comment_path(post, c), method: :delete, data: { confirm: "You sure?" } %>
          <!--comment => comment_path-->
      </div>
    <% end %>
```
コメント編集テンプレートを作成する。
```app/views/comments/edit.html.erb
<% provide(:title, "Edit Comment") %>
<% provide(:button_text, '更新') %>

<p class="margin-top">コメントする</p>
<%= render 'comment_form' %>
``` 
編集フォームのパーシャルを作成する。
・form_withにpostモデルとcommentモデルを渡すことで、/posts/:post_id/comments/:id　にアクセスするためのidを取得し、updateアクションを実行する。
・form_withにpostモデルのみ渡すことで、 /posts/:post_id/comments にアクセスするためのpost_idを取得し、createアクションを実行する。

下記を参考にした。

https://qiita.com/snskOgata/items/44d32a06045e6a52d11c

```app/views/comments/_comment_form.html.erb
<%= form_with(model:[@post, @comment]) do |f| %>
  <%= f.text_area :comment_content %>
  <%= f.hidden_field :post_id, value: @post.id %>
  <%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
```
# おまけ
ついでに、投稿編集・削除のパーシャルも載せちゃう。
```app/views/posts/_post_edit.html.erb
<% if current_user?(post.user) %>
<!--post => post_path-->
  <div class="link_to">
    <%= link_to "delete", post, method: :delete, data: { confirm: "You sure?" } %>
  </div>
  <%# 編集機能 %>
  <div class="link_to">
    <%= link_to "edit", edit_post_path(post) %>
  </div>
<% elsif current_user.id == post.user_id %>
  <div class="link_to">
    <%= link_to "delete", post, method: :delete, data: { confirm: "You sure?" } %>
  </div>
<% end %>
```

# 参考文献
https://programming-i.net/comment

https://zenn.dev/yukihaga/articles/aee4c55332a103