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