はじめに
投稿した内容に対してコメントをつける機能を導入する時のRuby on railsの流れがわかりづらかったので、まとめてみました。
スクール初学生さんも同じところで悩まれる方も多いと思いますので参考にして頂ければなと思います。
解決したい内容
投稿内容の詳細ページにコメントをつけるまでの流れを解説
前提条件
今回は
投稿したツイートを prototype
投稿に対するコメントを comment
とします。
全体の流れ
①まず初めにprototypeコントローラーのshowアクションにて@commentに空のインスタンスを生成
def show
@prototype = Prototype.find(params[:id])
@comment = Comment.new
@comments = @prototype.comments.includes(:prototype)
end
※ @prototypeは詳細画面を表示する情報があらかじめ入っていってshowのviewファイルに渡されるている。
②@commentsに@prototypeに結びついた全てのコメント情報とそれに結びつくユーザー情報を代入しshowのviewファイルに送る。
<main class="main">
<div class="inner">
<div class="prototype__wrapper">
<p class="prototype__hedding">
<%= @prototype.title %>
</p>
<%= link_to @prototype.user.name, root_path, class: :prototype__user %>
<% if user_signed_in? && current_user.id == @prototype.user_id %>
<div class="prototype__manage">
<%= link_to "編集する", edit_prototype_path(@prototype.id), class: :prototype__btn %>
<%= link_to "削除する", prototype_path(@prototype.id),data: { turbo_method: :delete }, class: :prototype__btn %>
</div>
<% end %>
<div class="prototype__image">
<%= image_tag @prototype.image %>
</div>
<div class="prototype__body">
<div class="prototype__detail">
<p class="detail__title">キャッチコピー</p>
<p class="detail__message">
<%= @prototype.catch_copy %>
</p>
</div>
<div class="prototype__detail">
<p class="detail__title">コンセプト</p>
<p class="detail__message">
<%= @prototype.concept %>
</p>
</div>
</div>
<div class="prototype__comments">
<%if user_signed_in? %>
<%= form_with model: [@prototype, @comment],local: true do |f|%>
<div class="field">
<%= f.label :content, "コメント" %><br />
<%= f.text_field :content, id:"comment_content" %>
</div>
<div class="actions">
<%= f.submit "送信する", class: :form__btn %>
</div>
<% end %>
<% end %>
③送られた情報は全て対象のインスタンス変数に代入される
<%= form_with model: [@prototype, @comment],local: true do |f|%>
<div class="field">
<%= f.label :content, "コメント" %><br />
<%= f.text_field :content, id:"comment_content" %>
</div>
<div class="actions">
<%= f.submit "送信する", class: :form__btn %>
</div>
<% end %>
④このコメントを入れるフォームに情報を入力すると@commentの中は現時点で空なのでコメントコントローラーのcreateアクションにパラメーターとして送られる。
注意点
※行き先をform_withのmodel:で定義しているがここで行き先を決めるルーティングを見るとプロトタイプのidが含まれないとcommentコントローラーのcreateアクションにパラメーターが送られない。
なのでmodel: [@prototype, @comment]という書き方をすることでパスにprototypeのidを含んだパラメーターとして送る事ができる。
(つまりはcommentのcreateアクションに飛ぶ事ができる)
Prefix Verb URI Pattern Controller#Action
prototype_comments POST /prototypes/:prototype_id/comments(.:format) comments#create
class CommentsController < ApplicationController
def create
@comment = Comment.new(comment_params)
if @comment.save
redirect_to prototype_path(@comment.prototype)
else
@prototype = @comment.prototype
@comments = @prototype.comments
render "prototypes/show"
end
end
private
def comment_params
params.require(:comment).permit(:content).merge(user_id: current_user.id, prototype_id: params[:prototype_id])
end
end
⑤送られた先のcreateアクションの処理
まずnewメソッドで送られてきたパラメーターをストロングパラメーターを用い必要なパラメーターが入った情報を@commentに代入する。
保存できた場合は、詳細の@commentが存在しているprototypeのshowページにいく。
(行き先のルートを@comment.prototypeとしているため)
redirect_toを用いているのでまたルーティング、purototypeのshowアクションの順に処理される(つまりは①番の処理に戻る。)
ダメだった場合の処理
@prototypeに@commentが存在しているprototypeの情報(レコード)のみを代入する。
( @prototype = @comment.prototype の意味)
次に@commentsに@prototypeに結びついている全てのcommentsを取得し代入する。
( @comments = @prototype.comments の意味)
(ここで単数複数系が違うのはアソシエーションの定義に準ずるため)
その二つのインスタンス変数を定義したものをprototypeのviewファイルに渡す。
なぜインスタンス変数@prototypeと@commentsを定義しているのか?
prototypeのshowアクション①番の処理でも同じように、この2つを変数を定義しているが、renderだとviewファイルにそのまま戻るので、戻るときに@を使わないと情報を渡せないためここでも行き先のviewファイルに渡すインスタンス変数@を定義する必要がある。
ダメだった場合は一つ前のviewファイルにelseで定義したインスタンス変数が送られる。
merge(user_id: current_user.id, prototype_id: params[:prototype_id])
この部分はcommentテーブルのuser_idカラムに、ログインしているユーザーのid(usersテーブルのログインしているレコードのid)が代入される。
そしてcommentテーブルのprototype_idに、paramsとして送られてきたルーティングのパラメーターprototype_idを代入している。