9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】投稿へのコメント機能を分かりやすく解説する。

Last updated at Posted at 2021-10-22

投稿へのコメント機能をいれたい

※ 前提として投稿機能を作成している必要があります ※ 
※ 投稿機能についてはこの記事では解説してません ※ 

投稿に対するコメント機能の実装を行いたい!
けど、初心者なのでどうやったらいいのかわからない。
カリキュラムを見たけどなんだか複雑でわからない。

そんな方のため投稿へのコメント機能を分かりやすく解説します^^

イメージとしてはこんな機能ができます。

イメージ図:
image.png

とある投稿に対して、コメントがついていますね!

それでは、いきます!

必要になる項目

投稿へのコメント機能に必要になるファイルはコチラ。

===============================

ステップ1:マイグレーションファイル
ステップ2:モデル
⇒ アソシエーションにつかいます

ステップ3:ルーティング
⇒ 投稿モデル等の親モデルにネストさせる必要があります。
こちらについては後ほど解説します。

ステップ4:コントローラー
ステップ5:ビューファイル

===============================

これらを順番に塔載していきます。

少し長くなるので、わからなくなったら
前に戻ったりして理解を深めてくださいね!

ステップ1:マイグレーションファイル

まずはマイグレーションファイルから記載していきましょう。

必要になるカラムは以下。
※ postモデルとuserモデルは作成済みの前提で進めます。

app/config/db/migrate/該当のマイグレーション.rb

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図で確認していきます!
(この図では会員・投稿・コメントの赤文字部分が対象のテーブルでアソシエーションでつないでいきます。)

image.png

php/app/models/user.rb

  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy

# ユーザーモデルは親の要素あたり1体多の関係に
# postモデルとcommentモデルの間にありますので
# dependet: :destroyです。

# つまりuserモデルが削除されると、投稿も消えるしコメントも
# 削除されてしまうということなのです!

php/app/models/user.rb

# postモデルへのアソシエーション
  has_many :posts, dependent: :destroy

# commentモデルへのアソシエーション
  has_many :comments, dependent: :destroy

# ユーザーモデルは親の要素あたり1体多の関係に
# postモデルとcommentモデルの間にありますので
# dependet: :destroyです。

# つまりuserモデルが削除されると、投稿も消えるしコメントも
# 削除されてしまうということなのです!

php/app/models/post.rb

 # userのアソシエーション
  belongs_to :user

  # 投稿のコメントへのアソシエーション
  has_many :comments, dependent: :destroy

# postsは親のモデルがuserなので、userがbelongs_toで
# 1つの投稿にはたくさんのコメントがつくのでhas_manyとなる!

php/app/models/comment.rb

  belongs_to :user
  belongs_to :post

# commentは親のモデルがuserとpostなので、
# userとpostがbelongs_toになります。

モデルに記述するアソシエーションはこの3つ書けばOKです!

ステップ3:ルーティング

ルーティングは少しざけ工夫が必要になります。
ネストという技をつかうわけなんですね。

まずは、どんなもんかお見せします!

php/app/config/routes.rb

    # 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で確認することができます。

image.png

rails infoのurlパターンでみるとわかりやすいですが、
urlが下記のようになっていますよね!
commentsに対してどの投稿idか?ということがurlに含まれるので
ネストする書き方をします。

/posts/:post_id/comments(.:format)

ステップ4:コントローラー

モデルとルーティングとマイグレーションファイルの記述が終わりましたので
いよいよコントローラーで処理を記載していきます!

まずは、コントローラー内の処理をみていきましょう♪

php/app/controllers/comments_controller.rb
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テーブルの書きをすべて
データベースに登録しなければいけません。

app/config/db/migrate/該当のマイグレーション.rb

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でします。
詳しく解説していきます!

php/app/models/user.rb
  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

以上がコントローラー内の記述となります!

php/app/views/posts/show.html.erb

<%= 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を特定できて削除できます!

まとめ

いかがでしたでしょうか?

投稿へのコメントはルーティング、コントローラー、ビューファイル内で少し
工夫した書き方が必要になってきますが理屈を理解できれば簡単になると思います。

もしわからない箇所や誤りがありましたらコメントくださいね!

それでは、チャオ!

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?