【Rails】コメント機能
本の投稿サイトにコメント機能を追加します。
いいね機能の実装も参考になったら嬉しいです。
実装機能の要件
実装機能の要件は以下の通りです。
コントローラ | アクション | 用途 |
---|---|---|
book_comments | create | コメントを作成する |
book_comments | destroy | コメントを削除する |
注意点:
- 自分のコメントしか削除できないこと
- コメント、コメント削除後は行う前の画面に遷移すること
モデル | 用途 | 備考 |
---|---|---|
book_comment | 投稿に対するコメントを管理する | 空のコメントは保存できないこと |
ビュー | 要素 |
---|---|
投稿一覧画面 | コメント数表示 |
投稿詳細画面 | 投稿に対するコメント一覧表示 |
コメント数表示 | |
ユーザー詳細画面 | コメント数表示 |
いいね機能のテーブル設計
以下のような、テーブル設計にします。
カラム名 | データ型 | カラムの説明 |
---|---|---|
id | integer | コメントごとのID |
comment | text | コメント本文 |
user_id | integer | コメントしたユーザーのID |
book_id | integer | コメントされた投稿のID |
Model作成
ターミナルでの入力方法は以下の通りです。
-
BookComment
モデルの生成
rails g model BookComment comment:text user_id:integer book_id:integer
上記のコマンドを実行すると、db/migrate
ディレクトリにマイグレーションファイルが生成されます。
- データベースへのマイグレーション実行
rails db:migrate
上記のコマンドを実行することで、マイグレーションファイルに基づいてデータベースにテーブルが作成されます。
以上の手順により、BookComment
モデルが作成され、comment
とuser_id
、book_id
のカラムが含まれるテーブルがデータベースに作成されます。
関連付けの設定
BookComment
モデルと他のモデルの関連付けを設定します。
モデルの関係性
図表で表すと以下の通りです。
-
User
(ユーザー)とBookComment
(コメント)の関係は、1対多の関係です。1人のユーザーが複数のコメントを行えます。 -
Book
(投稿)とBookComment
(コメント)の関係も、1対多の関係です。1つの投稿が複数のコメントを持つことができます。
つまり、ユーザーとコメントの関係は「1:N」であり、投稿とコメントの関係も「1:N」です。
関連付けの実装
以下のようにそれぞれのモデルに関連付けを記述します。
- BookCommentモデル
class BookComment < ApplicationRecord
belongs_to :user
belongs_to :book
validates :comment, presence: true
end
上記のコードでは、BookComment
モデルがUser
モデルとBook
モデルに対してそれぞれ1対多の関連付けを持つことを表しています。
また、validates :comment, presence: true
を記載することでバリデーションの定義をします。ここでは、空のコメントが保存されないようにします。
- Userモデル
class User < ApplicationRecord
has_many :book_comments, dependent: :destroy
end
- Bookモデル
class Book < ApplicationRecord
has_many :book_comments, dependent: :destroy
end
上記のコードでは、User
モデル、Book
モデルにも同様にhas_many :BookComment
の関連付けを定義しています。これにより、User
モデル、Book
モデルのインスタンスは複数のBookComment
モデルのインスタンスと関連付けられます。また、dependent: :destroy
オプションによって、User
やBook
が削除された場合に関連するBookComment
も同時に削除されます。
以上の設定により、User
モデルとBook
モデルはそれぞれ複数のBookComment
モデルと関連付けられ、1対多の関係が成立します。
Routing
以下の通りルーティングの設定をします。
Rails.application.routes.draw do
:
resources :books, only: [:index, :show, :edit, :create, :destroy, :update] do
+ resources :book_comments, only: [:create, :destroy]
resource :favorites, only: [:create, :destroy]
end
resources :users, only: [:index, :show, :edit, :update]
end
コメントは投稿に対して関連付けられるためbooks
と`book_commentsの親子関係を設定します。コメントは特定の投稿に紐付いており、その投稿の下にコメントを追加する形で表示や操作を行いたいからです。
Controller作成
ターミナルで以下のコマンドを実行してコントローラを作成します。
rails g controller book_comments
book_comment_controllerへの記述
book_comments_controller.rb
ファイルに以下のコードを記述します。
class BookCommentsController < ApplicationController
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 request.referer
end
def destroy
comment = BookComment.find(params[:id])
comment.destroy
end
private
def book_comment_params
params.require(:book_comment).permit(:comment)
end
end
以下の通りコードの解説をします。
-
create
メソッドは、新しいコメントを作成するためのアクションです。まず、URLのbook_idから関連する本を見つけます。次に、現在のユーザーによる新しいコメントを作成します。comment.book_id = book.id
で作成したコメントに関連する本のIDを設定し、保存します。最後に、リクエスト元のページにリダイレクトします。 -
destroy
メソッドは、コメントを削除するためのアクションです。与えられたコメントのIDを使用して、対応するコメントを見つけて削除します。その後、リクエスト元のページにリダイレクトします。 -
book_comment_params
は、ストロングパラメータを使用して、安全なパラメータのみを受け入れるようにしています。コメント作成時に必要なcomment
パラメータのみを許可しています。
books_controllerへの記述
コメントを投稿するためのインスタンス変数を定義します。
class BooksController < ApplicationController
:
def show
@book = Book.find(params[:id])
@books = Book.new
@user = @book.user
+ @book_comment = BookComment.new
end
:
end
今回は本の詳細ページであるshowアクションで、コメント投稿フォームを表示します。
そのため、上述のコードを追加することで、新しいコメントの投稿フォームを表示できるようします。
Viewページ
コメント投稿ページと、コメント件数の表示を記述していきます。
コメント投稿ページ
今回は部分テンプレートファイルで作成します。
- コメント投稿フォーム
<%= form_with model: [book, book_comment] do |f| %>
<%= f.text_area :comment, rows:'5', placeholder: "コメントをここに", class: "w-100" %>
<%= f.submit "送信" %>
<% end %>
解説:
-
<%= form_with model: [book, book_comment] do |f| %>
form_withに対して、[book, book_comment]のように、配列で2つの変数を指定しています。
特定の本に対してコメントを投稿する場合、そのコメントはその本と関連付けられます。この場合、コメント投稿フォームはコメントの内容だけでなく、どの本に対するコメントなのかもフォームで指定する必要があります。 -
<%= f.text_area :comment, rows:'5', placeholder: "コメントをここに", class: "w-100" %>
rows:'5'
:テキストエリアを5行に表示しています。
placeholder: "コメントをここに"
:テキストエリア内に表示されるテキストを指定します。このテキストは、ユーザーに対して入力する内容のヒントを与える役割があります。
class: "w-100"
:Bootstrapのクラスで、テキストエリアの幅を100%(width: 100%)に設定します。
- コメント一覧部
<table>
<tbody>
<% book.book_comments.each do |book_comment| %>
<tr>
<td>
<%= link_to user_path(book_comment.user) do %>
<%= image_tag book_comment.user.get_profile_image, size: "50x50" %><br>
<%= book_comment.user.name %>
<% end %>
</td>
<td><%= book_comment.comment %></td>
<td>
<% if book_comment.user == current_user %>
<%= link_to "Destroy", book_book_comment_path(book, book_comment), method: :delete, class: "btn btn-sm btn-danger float-end" %>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
解説:
コメント一覧を表示し、各コメントにはユーザーのプロフィール画像と名前を表示します。コメントの所有者のみが削除ボタンを表示し、ボタンを右側に寄せています。
コメント投稿ファイルの呼び出しとコメント件数表示
先ほどの部分テンプレートファイルを詳細画面から呼び出します。
また、詳細画面と一覧画面にそれぞれにコメント件数を表示する記述を追加します。
- 詳細画面
<div class="container">
<div class="row">
<div class="col-md-3">
<h2>User info</h2>
<%= render "users/info", user: @user %>
<h2 class="mt-3">New book</h2>
<%= render "form", book: @books %>
</div>
<div class='col-md-8 offset-md-1'>
<h2>Book detail</h2>
<table class="table">
<tr>
<td><%= link_to @book.user do %>
<%= image_tag @book.user.get_profile_image, size:"100x100" %><br>
<%= @book.user.name %>
<% end %>
</td>
<td><%= link_to @book.title, @book %></td>
+ <td>コメント数: <%= @book.book_comments.count %></td>
<td><%= @book.body %></td>
<td><%= render "favorites/btn", book: @book %></td>
<% if @book.user == current_user %>
<td><%= link_to "Edit", edit_book_path(@book), class: "btn btn-sm btn-success" %></td>
<td><%= link_to "Destroy", @book, method: :delete, data: { confirm: "本当に消しますか?" }, class: "btn btn-sm btn-danger" %></td>
<% end %>
</tr>
</table>
+ <%= render "book_comments/index", book: @book %>
+ <%= render "book_comments/form", book: @book, book_comment: @book_comment %>
</div>
</div>
</div>
- 一覧画面
<table class="table table-hover table-inverse">
<thread>
<tr>
<th></th>
<th>Title</th>
<th>Opinion</th>
<th colspan="3"></th>
</tr>
</thread>
<tbody>
<% books.each do |book| %>
<tr>
<td><%= link_to book.user do %>
<%= image_tag book.user.get_profile_image, size:'50x50' %>
<% end %>
</td>
<td><%= link_to book.title,book %></td>
<td><%= book.body %></td>
<td><%= render "favorites/btn", book: book %></td>
+ <td>コメント数: <%= book.book_comments.count %></td>
</tr>
<% end %>
</tbody>
</table>
コメント機能の実装でした。
Discussion