投稿へのコメント機能をいれたい
※ 前提として投稿機能を作成している必要があります ※
※ 投稿機能についてはこの記事では解説してません ※
投稿に対するコメント機能の実装を行いたい!
けど、初心者なのでどうやったらいいのかわからない。
カリキュラムを見たけどなんだか複雑でわからない。
そんな方のため投稿へのコメント機能を分かりやすく解説します^^
イメージとしてはこんな機能ができます。
とある投稿に対して、コメントがついていますね!
それでは、いきます!
必要になる項目
投稿へのコメント機能に必要になるファイルはコチラ。
===============================
ステップ1:マイグレーションファイル
ステップ2:モデル
⇒ アソシエーションにつかいます
ステップ3:ルーティング
⇒ 投稿モデル等の親モデルにネストさせる必要があります。
こちらについては後ほど解説します。
ステップ4:コントローラー
ステップ5:ビューファイル
===============================
これらを順番に塔載していきます。
少し長くなるので、わからなくなったら
前に戻ったりして理解を深めてくださいね!
ステップ1:マイグレーションファイル
まずはマイグレーションファイルから記載していきましょう。
必要になるカラムは以下。
※ postモデルとuserモデルは作成済みの前提で進めます。
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.integer :user_id, null: false
t.integer :post_id, null: false
t.text :content, null: false
t.timestamps
end
end
end
それでは解説をしていきます。
user_id ⇒ 投稿に対してコメントを書いた人のuser_id
post_id ⇒ 投稿のid
content ⇒ 投稿内容
post_idは後ほども解説しますが、
ルーティングでネストすることによって取得できます。
ここでは、そうなんだ~程度に覚えといてください。
完了してら、rails db migrateをしてテーブルを
作成しちゃってくださいね♪
これでテーブルは作成完了!
ステップ2:モデル
次はアソシエーションをするためにモデルの記述をしていきます!
投稿モデルは3つのモデルに記述が必要になります。
1:投稿者のモデル(当記事ではUserモデル)
2:投稿モデル(コメントする記事の大元のモデル)
3:コメントモデル(投稿に対するコメントしたモデル)
・
・
・
それでは、アソシエーションを書いていきます。
とその前に全体の繋がりをER図で確認していきます!
(この図では会員・投稿・コメントの赤文字部分が対象のテーブルでアソシエーションでつないでいきます。)
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
#ユーザーモデルは親の要素あたり1体多の関係に
#postモデルとcommentモデルの間にありますので
#dependet: :destroyです。
#つまりuserモデルが削除されると、投稿も消えるしコメントも
#削除されてしまうということなのです!
#postモデルへのアソシエーション
has_many :posts, dependent: :destroy
#commentモデルへのアソシエーション
has_many :comments, dependent: :destroy
#ユーザーモデルは親の要素あたり1体多の関係に
#postモデルとcommentモデルの間にありますので
#dependet: :destroyです。
#つまりuserモデルが削除されると、投稿も消えるしコメントも
#削除されてしまうということなのです!
# userのアソシエーション
belongs_to :user
# 投稿のコメントへのアソシエーション
has_many :comments, dependent: :destroy
#postsは親のモデルがuserなので、userがbelongs_toで
#1つの投稿にはたくさんのコメントがつくのでhas_manyとなる!
belongs_to :user
belongs_to :post
#commentは親のモデルがuserとpostなので、
#userとpostがbelongs_toになります。
モデルに記述するアソシエーションはこの3つ書けばOKです!
ステップ3:ルーティング
ルーティングは少しざけ工夫が必要になります。
ネストという技をつかうわけなんですね。
まずは、どんなもんかお見せします!
# devise用のurl
devise_for :users
# users用のURL用に設定
resources :users
resources :posts do
resources :comments, only: [:create, :destroy]
end
見てわかる通り、postの中にdoとendで囲ってcommentsが入っていますね。
こういう記述をすることでpostのurlの中にcommentのurlも含められるんです。
この記述はコントローラーでの投稿データのidを取得するのにも必要になるのですが、commentのurlはpostに紐づいていますよ!ということもurlで確認することができます。
rails infoのurlパターンでみるとわかりやすいですが、
urlが下記のようになっていますよね!
commentsに対してどの投稿idか?ということがurlに含まれるので
ネストする書き方をします。
/posts/:post_id/comments(.:format)
ステップ4:コントローラー
モデルとルーティングとマイグレーションファイルの記述が終わりましたので
いよいよコントローラーで処理を記載していきます!
まずは、コントローラー内の処理をみていきましょう♪
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@comment = Comment.new(comment_params)
@comment.user_id = current_user.id
@comment.post_id = @post.id
@comment.save
end
def destroy
@post = Post.find(params[:post_id])
@post_comments = @post.comments
Comment.find_by(id: params[:id], post_id: params[:post_id]).destroy
end
private
def comment_params
params.require(:comment).permit(:content)
end
end
コントローラーのcreateから見ていきましょう!
まず大前提として、createする際にcommentテーブルの書きをすべて
データベースに登録しなければいけません。
class CreateComments < ActiveRecord::Migration[5.2]
def change
create_table :comments do |t|
t.integer :user_id, null: false
t.integer :post_id, null: false
t.text :content, null: false
t.timestamps
end
end
end
use_idとpost_idとcontentを埋めるための記述をcreateでします。
詳しく解説していきます!
def create
@post = Post.find(params[:post_id])
⇒ アクセスした投稿のurlからpost_idを取得します。
例えば、投稿記事のurlが/posts/6であるとcommentを作成するときに
id6の記事を探し出してきます。
@comment = Comment.new(comment_params)
⇒ 投稿前には.newでカラムの箱を用意しないといけないので
ストロングパラメーターのcomment_paramsを記述してcontentの空のカラムを用意します。
@comment.user_id = current_user.id
⇒ commentカラムにはuser_idがあるので現在ログインしているユーザーのidを
current_user.idでuser_idカラムに代入します。
@comment.post_id = @post.id
⇒ commentカラムにはpost_idがあるので現在アクセスしている
記事のidをpost_idに登録したいので@postですでに投稿のidを取得しているので
投稿記事自体のidだけをpost_idで格納してあげます。
ちなみにfindメソッドは今回の場合ですと、投稿記事自体のidだけでなく
すべての情報つまり、投稿テーブル(今回は記述をこの記事では省いてます)の投稿内容やuser_id
など引っ張ってきているので今回は投稿のidだけが必要なので@post.idと書くことで
idだけを格納することができるようになってます!
@comment.save
最後に@comment.saveと書くことでデータを保存できます。
end
def destroy
@post = Post.find(params[:post_id])
削除する場合もどうようにまずは投稿記事のidを探します。
Comment.find_by(id: params[:id], post_id: params[:post_id]).destroy
そして、削除する際は少し工夫が必要です。
思い出してださい。commentテーブルにはuser_id, post_id, contentがあったはずです。
それらのうちuser_idとpost_idを指定して削除する必要があります。
なのでid: params[:id]というのはコメントのidで、params[:post_id]というのは
投稿記事のidを記述しています。
end
private
def comment_params
params.require(:comment).permit(:content)
ちなみにストロングパラメーターですが、:user_idと:post_idがないじゃん?
お気づきになった方もいるかもしれませんが、実はcreateのコントローラー内で
@comment.user_id = current_user.idと@comment.post_id = @post.idを記載しており
ストロングパラメーターに書かなくてもOkなんです。
ストロングパラメータに書かないといけないのはフォームから飛んできたデータには
書かないといけませんが、user_idとpost_idはフォームから飛んでこないので書かなくてもOK。
※書いても作動はします。
end
end
以上がコントローラー内の記述となります!
<%= form_with model:[@post, @comment], local: false do |f| %>
<%= f.text_area :content, rows:'5',placeholder: "Any comments on this post?", class:"form-control mt-3" %>
<div class="text-right">
<%= f.submit "Submit", class: "btn btn-primary mt-3 mb-3" %>
</div>
<% end %>
form_withする際のインスタンスは@postと@commentの両方が必要になります。
これは、@postは投稿のインスタンス変数で@commentはコメントのインスタンス変数になります。
postコントローラーのshowアクションに記載するコードは下記の通り。
@post = Post.find(params[:id])
@comment = Comment.new
<% @post_comments.each do |comment| %>
<div class="col-3 text-center">
<%= attachment_image_tag comment.user, :profile_image, fallback: "no_image.jpg", size: '40x40' , class:"rounded-circle"; %>
<div class="small mb-4"><%= comment.user.nickname %>
</div>
</div>
<%= link_to "Delete", post_comment_path(comment.post, comment), method: :delete, remote: true, "data-confirm" => t('comments.create.delete_confirm'), class: "btn-sm btn-danger" %></td>
<% end %>
@post_commentsというのは、投稿に紐づくコメントをすべて取得するためのインスタンス変数。
なのでpostモデルにインスタンス変数として、@post_comments = @post.commentsようにpostコントローラーに記述が必要です。
※ commentsはアソシエーションで@postに紐づくすべてのコメントを取得しています。
それをeach文で回すことで投稿に対するコメントをひとつずつ表示できます。
削除する際はpost_comment_pathに記事のidとコメントのidが二つ必要になってきます。なのでコメントのidはcommentで取得でき
投稿のidはアソシエーションを使って、commentに紐づくpost(投稿のid)を二つ記載すればOK。
これでpost_idとcomment_idを特定できて削除できます!
まとめ
いかがでしたでしょうか?
投稿へのコメントはルーティング、コントローラー、ビューファイル内で少し
工夫した書き方が必要になってきますが理屈を理解できれば簡単になると思います。
もしわからない箇所や誤りがありましたらコメントくださいね!
それでは、チャオ!