🐣

[Ruby on Rails] コメント機能の作成

に公開

テーブルの設計

カラム名 データ型 カラムの説明
comment text コメント本文
user_id integer コメントをしたユーザーの id
book_id integer コメントされた投稿の id

モデルの作成

$ rails g model BookComment comment:text user:integer book_id:integer

モデルを作成した際にテーブルも作成したので、マイグレーションでデータベースに反映させる

$ rails db:migrate

モデルに関連付けを行う

user.rb
has_many :book_comments, dependent: :destroy
book.rb
has_many :book_comments, dependent: :destroy
book_comment.rb
belongs_to :user
belongs_to :book

コメント機能は user, book 共に 1対N の関係になるのでこのような書き方になります

コントローラーの作成

$ rails g controller book_comments

今回コメント機能は book/show 画面に設定するので、 アクションは create destroy
の2つになります。

book_comments_controller.rb
  def create
    book = Book.find(params[:book_id])
    comment = current_user.book_comments.new(book_comment_params)
    comment.book_id = book.id
    comment.save
    redirect_to book_path(book.id)
  end

  def destroy
    BookComment.find(params[:id]).destroy
    redirect_to request.referer
  end

  private

  def book_comment_params
    params.require(:book_comment).permit(:comment)
  end

解説

create

book = Book.find(params[:book_id])

  • Bookモデルの id を取得

comment = current_user.book_comments.new(book_comment_params)

  • ログインしているユーザーに紐付けて、空のインスタンスを作成し、params で指定したカラムを追加
  • この部分は 以下の2つと同じ意味です
    comment = PostComment.new(post_comment_params)
    comment.user_id = current_user.id 

comment.book_id = book.id

  • コメントされた投稿の id と  bookのオブジェクトの関連付けをしている

comment.save

  • コメントを保存

redirect_to book_path(book.id)

  • コメントをした後は book/show 画面に遷移するように設定
destroy

BookComment.find(params[:id]).destroy

  • BookComment モデルから idをもらい、削除する

redirect_to request.referer

  • request.referer はアクションを実行したページに遷移させるので、同じページに遷移される
book_comment_params

params.require(:book_comment).permit(:comment)

  • params formから送られてくるデータが入っています
  • require 送られてきたデータの中からモデル名を指定し、データを絞り込みます
  • permit requireで絞り込んだデータの中から、保存するカラムを指定します

ルーティング

config/routes.rb
  resources :book, only: [:new, :create, :index, :show, :destroy] do
    resources :book_comments, only: [:create, :destroy] #ここを追加
 end
  • 親の resource の中に do と end を使用して、子の resource を書くことを
    ネストすると言う
  • 上記のような書き方をすることで、 params[book_id] で Bookの idを取得できるようになる

コメントを投稿するためのインスタンス変数を定義する

books_controller
@book = Book.find(params[:id])
@book_comment = BookComment.new

Book モデルから id をもらいます
コメントを投稿するために新しいインスタンスを定義します

view 画面を作成する

books/show.html.erb
<table>
<% if @book.book_comments.exists? %>
<% @book.book_comments.each do |book_comment| %>
<tr>
  <td>
      <%= book_comment.user.name %>
  </td>
  <td>
      <%= book_comment.comment %>
  </td>
      <% if book_comment.user == current_user %>
        <td>
          <%= link_to "Destroy", book_book_comment_path(book_comment.book, book_comment), method: :delete %>
        </td>
      <% end %>
   <% end %>
 </tr>
 <% end %>
</table>

<%= form_with model: [@book, @book_comment] do |f| %>
  <%= f.text_area :comment %>
  <%= f.submit "送信" %>
<% end %>

解説

<table>の中身

<% if @book.book_comments.exists? %>

  • if はもし〇〇なら△△を実行という意味
  • exists? は存在するかどうかを返します

<% @book.book_comments.each do |book_comment| %>

  • 選択した投稿にあるコメントを1つ1つ取り出します

<%= book_comment.comment %>

  • each で取り出したbook_comment のcomment を表示します

<% if book_comment.user == current_user %>

  • もしコメントしたユーザーがログインしているユーザーなら表示します

<%= link_to "Destroy", book_book_comment_path(book_comment.book, book_comment), method: :delete %>

  • link_to でリンクを作成
  • book_book_comment_path はbook_commentsのdestroyアクションのpath
  • delete を使用する際は、最後にmethod を指定する
form_with

` <%= form_with model: [@book, @book_comment] do |f| %>

  • form_with ヘルパーを使用し、入力欄を作成します
  • model オプションを使用し、 @book @book_comment の情報を追記します
  • ブロック変数 f をモデルオブジェクトを作成します

<%= f.text_area :comment %>

  • f.text_area は複数行のテキストフィールドを作成します
  • :comment は @book_comment にある commentカラムと判断される

view 画面でコメント数を表示

コメント数:<%= @book.book_comments.count %>

これで表示させられます。

Discussion