Rails+Ajax+コメント機能を実装! Railsポートフォリオ制作日記②

日記のくせに書く頻度が少ないかな。

という事で、コメント機能を実装してみたのでまとめていきます。

レッツゴー

またこちらの記事を参考にしてほとんど一緒になってしまいましたが自分なりに書いたのでご了承ください。

ysk-pro.hatenablog.com

 

前提

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

前提として、投稿詳細ページへのコメントを実装しました。

デザインもやっていないのでカスタマイズは自分流にアレンジして下さいね。

完成イメージはこんな感じです!

f:id:yamasey712:20190917020520p:plain

コメント機能

 

また、コメントを表示するページ(僕の場合はshowページ)の実装と、JQueryの読み込みはできてる前提で行きます!

 

2 コメントテーブル作成

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

$ rails g model comment content:string user:references micropost:references

 UserモデルとMicropostモデルと関連づけるためreferencesも書く。

config/db/schema.rb

create_table "comments", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
  t.string "content"
  t.bigint "user_id"
  t.bigint "micropost_id"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.index ["micropost_id"], name: "index_comments_on_micropost_id"
  t.index ["user_id"], name: "index_comments_on_user_id"
 end

 

 うん。しっかりとuser_idとmicropost_idがある。

ちなみに、『micropost_id』がどの投稿に対するコメントであるかを格納するため

『user_id』が誰の投稿であるかを格納するためになります。

安心できたところで忘れずに

$ rails db:migrate 

 

3 モデルの設定 

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

 

app/models/comment.rb

class Comment < ApplicationRecord
belongs_to :user
belongs_to :micropost
validates :content, presence: true
end

 例えばコメント①はただ1人のユーザーとただ1つの投稿に結びつく1対1の関係であるため『belongs_to』で結びつけます。

 

app/models/user.rb

class User < ApplicationRecord
has_many :microposts, dependent: :destroy
has_many :comments, dependent: :destroy

 

app/models/micropost.rb

class Micropost < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy

 

逆にUserモデルやMicropostモデルは1人のユーザーが何個もコメントする事ができ、1つの投稿は何個もコメントを持つ事ができる1対多の関係であるため『has_many』で結びます。また、『dependent: :destroy』は投稿が消えたらコメントもついでに消してね。って伝えてます。

 

4 ルーティング

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

config/routes.rb

resources :microposts, only: [:new, :show, :index, :create, :destroy] do
  resources :comments, only: [:create, :destroy]
 end

 コメントがどの投稿にされたかを識別するため、ルーティングのURLに投稿のIDが

必要になります。つまり『/micropost/1/comment/』こんな感じにしたい。

そのためにはルーティングをこのように記述する必要がある。(ネストすると言う)

ちなみにこの1は『micropost_id』のこと。

 

5 コメントコントローラー

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

app/controllers/comment.rb

class CommentsController < ApplicationController
 def create
  @micropost = Micropost.find(params[:micropost_id]) #1
  @comment = @micropost.comments.build(comment_params) #2
  @comment.user_id = current_user.id #3
  if @comment.save
  render :comments #4
  end
 end

 def destroy
  @comment = Comment.find(params[:id]) #5
  if @comment.destroy
  render :comments #6
  end
 end


 private
  def comment_params
   params.require(:comment).permit(:content)
  end
end

 

#1:コメントをする対象の投稿(micropost)のインスタンスを作成します。

#2:「.build」を使うことで、@micropostのidをmicropost_idに含んだ形でcommentインスタンスを作成します。

#3:現在のuserのidを入れます。

#4:保存がされると、render :commentsによって「app/views/comments/comment.js.erb」を探しにいきます。
「form_with」でフォームを送信した時は、デフォルトでjsファイルを探しにいく設定になっています。
htmlファイルを探しにいってほしい場合には、form_withの後に「local: true」と記載する必要があります。

#5:削除する対象のコメントインスタンスを探します。

#6:削除がされると、「comments.js.erb」を探しにいきます。
削除のリンクを記載している「link_to」の中に「remote: true」を記載していることでjsファイルを探しにいってくれます。
7. app/views/comments/_comments.html.erb に記載しています。
「remote: true」を記載していなかった場合は、htmlファイルを探しにいきます。

 

6 投稿のコントローラー

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

app/controllers/microposts_controller.rb

def show
@micropost = Micropost.find(params[:id])
@comment = Comment.new #1
@comments = @micropost.comments #2
end

 

どちらも、7. 投稿のビュー「app/views/microposts/show.html.erb」でパーシャルに渡す変数として使用します。
#1:入力フォームで使用するインスタンスを作成しています。

#2:コメント一覧表示で使用するためのコメントデータを入れています。

つまり下準備の段階です。

 

7 投稿のビューページ

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

app/views/microposts/show.html.erb

<div>
<h4>コメント</h4>
<div id="comments_area"> #1←こいつがのちに重要になる
  #コメント一覧を表示させるrender#
<%= render partial: 'comments/comments', locals: { comments: @comments } %>
</div>
<% if user_signed_in? %>
#コメント入力フォームを表示させるためのrender#
<%= render partial: 'shared/comments_form', locals: { comment: @comment, micropost: @micropost } %>
<% end %>
</div>

 

#1:「id="comments_area"」がポイントです。
このidをターゲットにして、このdiv内をAjaxで書き換えます。
このdivの内側に、renderを使ってパーシャルを表示します。
@commentをパーシャル内で使うローカル変数commentとして渡しています。

 

8 パーシャル部分のビュー

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

app/views/comments/_comments.html.erb

<% comments.each do |comment| %>
<% unless comment.id.nil? %>
<p><%= link_to "#{comment.user.name}さん", "#" %></p>
<p>コメント:<%= comment.content %></p>
<% if comment.user == current_user %>
<p><%= link_to 'コメントを削除する', micropost_comment_path(comment.micropost_id, comment.id), method: :delete, remote: true %></p>
<% end %>
<% end %>
<% end %>

 

先ほど投稿のビューで渡したローカル変数(comments)をeach分で渡してあげてコメントがあれば全てを表示させるようにします。

ポイントは、コメントの削除のところで「(comment.micropost_id, comment.id)」と投稿のidとコメントのidを渡す必要があることと、5. コメントコントローラーのところでも触れましたが「remote: true」をつけることによって、コントローラーでjsファイルを探しにいってもらうことです。
idをcomment.micropost_idとcomment.idの2つ渡す必要があるのは、削除したいコメントを指定するには「micropost/1/comment/1」のようにmicropost_idとcomment_idを指定する必要があるためです。

app/views/comments/comments_form.html.erb

<%= form_with(model: [micropost, comment] ) do |form| %>
<div>
<%= form.text_area :content %>
</div>
<div class="actions">
<%= form.submit "コメントをする" %>
</div>
<% end %>

 ポイントは、「model: [micropost, comment]」とすることです。
micropost, commentはそれぞれ、7. 投稿のビューで渡しているインスタンスのローカル変数です。
投稿に紐づいたコメントを生成するため、ここでpost、microcommentのインスタンスを渡すことが必要になります。

 

9 jsファイル

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

app/views/comments/comments.js.erb

$("#comments_area").html("<%= j(render 'comments', { comments: @comment.micropost.comments }) %>")
$("textarea").val('')

 

このファイルに、7. 投稿のビューの中で id = "comments_area"とした箇所を書き換える処理を記載しています。

「$("#comments_area")」が id = "comments_area"をターゲットとする記載です。
ターゲットとした箇所を、「render 'comments'」で指定している8. パーシャル部分のビューの内容で書き換えています。

{ comments: @comment.micropost.comments }で、@comment.micropost.comments をローカル変数 comments に入れて渡しています。
@comment.micropost.comments は、コメント一覧表示するのに必要なコメント全件です。

「$("textarea").val('')」によって、コメント入力後のコメント入力欄を空にしています。

 

10 最後に

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

ほとんど書いてあること同じじゃねーかあ!!って言われても仕方ありませんがこれはあくまで自分自身が忘れないためにまとめていますので大目にみてください。

おそらく、この通りにやってもうまくいかないこともあると思います。

そんなことで諦めず、あーでもないコーデもないと試行錯誤しながらやっていくうちに機能するようになります。(きっとね)

その達成感と行ったら一人でガッツポーズしちゃうぐらいだから相当嬉しいよね。

だからやめられない。

という事でこれ書いてたらもう夜中の3時だから寝なきゃだね。

それじゃあおやすみ