Rails 掲示板にコメント機能を追加 part2

part1からの続き。
掲示板の詳細画面を実装したので、そこにコメント機能を実装していく。

投稿詳細画面へコメントの入力フォーム、エリアを追加する。
# Controllerへ追記。

boards_controllerにインスタンス変数@commentと@commentsを定義し、view側で呼び出す。

class BoardsController < ApplicationController
  skip_before_action :require_login, only: %i[index create show]
  def show
    if logged_in?
      @board = Board.find(params[:id])
      @comment = Comment.new
      @comments = @board.comments.includes(:user).order(created_at: :desc)
    else
      redirect_back_or_to login_path, danger: 'ログインしてください'
    end
  end

logged_in?メソッドを使い、ログインしていない場合 ログインページにリダイレクトされるようにした。
ここは単純にskip_before_action :require_login, only: %i[index create]で良かった。
コメントを習得する際はN+1問題に注意し、includesメソッドを使用しユーザーを呼び出す。
created_at: :descで作成された順に降順で表示。

コメントフォームのview

パーシャルで作成する。
ルーティングをネスト(board/board_id/comment)している場合はurlを2つ指定する。

<!-- コメントフォーム -->
<div class="row mb-3">
  <div class="col-lg-8 offset-lg-2">
    <%= form_with model: comment, url: [board, comment], local: true do |f| %>
      <%= render 'shared/error_messages', object: f.object %>    
      <%= f.label :body %>
      <%= f.text_area :body, class: 'form-control mb-3', rows: 4,  placeholder: 'コメント' %>
      <%= f.submit class: 'btn btn-primary', value: '投稿' %>
    <% end %>
  </div>
</div>   

コメントエリアのview

こちらもパーシャルで作成。
コメントを作成したユーザーにだけアイコンを表示。

<!-- コメントエリア -->
<%= comment.user.decorate.full_name %>
<%= simple_format(comment.body) %>
#入力値の改行に対応する
〜
 <% if current_user.own?(comment) %>  
#自分のコメントだけ編集、削除アイコンを表示する
<td class="action">
  <ul class="list-inline justify-content-center" style="float: right;">
    <li class="list-inline-item">
      <a href="#" class='js-edit-comment-button' data-comment-id="<%= comment.id %>">
        <%= icon 'fa', 'pen' %>
      </a>
    </li>
    <li class="list-inline-item">
      <a href="#" class='js-delete-comment-button' data-comment-id="<%= comment.id %>">
        <%= icon 'fas', 'trash' %>
      </a>
    </li>
   </ul>
 </td>
<% end %>
  • コメントエリア_comment.html.erbにbootstrapを適応。
    render commentsでよしなに_comment.html.erbを呼び出している。
    render @commnetsでも同様。
<div class="row">
  <div class="col-lg-8 offset-lg-2">
    <table id="js-table-comment" class="table">
      <%= render comments %>
    </table>
  </div>
</div>

掲示板一覧画面と掲示板詳細画面において、コメントを作成したユーザーにのみ編集、削除ボタンを表示させる。

  • モデル側にユーザーのコメントであるか判定するメソッドを定義。
  def own?(object)
    id == object.user_id
  end
  • 削除、編集ボタンのパーシャルを作成。
    掲示板の編集と削除のボタンは、掲示板の一覧と詳細ページで同じものを表示する。
<ul class='crud-menu-btn list-inline float-right'>
  <li class="list-inline-item">
      <%= link_to '#', id: "button-edit-#{board.id}" do %>
      <%= icon 'fa', 'pen' %>
    <% end %>
  </li>
  <li class="list-inline-item">
    <%= link_to '#', id: "button-delete-#{board.id}" do %>
      <%= icon 'fas', 'trash' %>
    <% end %>
  </li>
</ul>

掲示板詳細画面show.html.erb

  • 編集削除ボタン
  • コメントフォーム
  • コメントエリア
    を読み込む。
    フォームは、comments/_form.html.erbをレンダーし、@boardと@commentを引数として渡している。
    エリアは、comments /_comments.html.erbをレンダーし、@commentsをcommentsという変数で使えるようにしている。
<!-- 編集、削除ボタン-->
<%= render 'crud_menus', board: @board %>
<!-- コメントフォーム -->
<%= render 'comments/form',{board: @board, comment: @comment} %> 

<!-- コメントエリア -->
<%= render "comments/comments", {comments: @comments} %>
  • 掲示板一覧画面に編集、削除ボタンを読み込む。
<%= render 'crud_menus', board: board %>

comments_controllerの作成。

$ rails g controller comments
userとcommentでアソシエーションが定義されてるのでその点に注意する。

class CommentsController < ApplicationController
  def create
    comment = current_user.comments.build(comment_params)
    if comment.save
      redirect_to board_path(comment.board), success: 'コメントを作成しました'
    else
      redirect_to board_path(comment.board), danger: 'コメントを作成できませんでした'
    end
  end
end

ログインユーザーに関連したコメントを表示させる。
current_userメソッドでログイン中のユーザーを返す。
アソシエーションで定義されたオブジェクトを初期化する際はnewではなくbuildを使う。

 comment = current_user.comments.build(comment_params)

ストロングパラメータの追加。

コメントをデータベースへ保存するにはuser_idとboard_idが必要でcommentの値(body)とboard_idが保存される。
(board_id: params[:board_id])はURLであるboards/:board_id/commentsからboard_idをとってきている。
mergeはハッシュを合体させるメソッド。

  private

  def comment_params
    params.require(:comment).permit(:body).merge(board_id: params[:board_id])
  end