2021/01/04 shallowオプションとか
ネスとしたルーティングに対してのform_with
resources :boards, only: %i[index new create show] do resources :comments, shallow: true end
としてルーティングを作成。
form_withでcommentに対してpostリクエストを送りたい。
def show @board = Board.find(params[:id]) @comment = Comment.new end フォーム用に@comment変数を作成 <%= form_with model: [@board, @comment], local: true do |f| %> <%= f.label :body %> <%= f.text_area :body, class: "form-control mb-3", rows: 4 %> <%= f.submit t('.submit'), class: "btn btn-primary" %> <% end %> モデルの引数に [ 親 , 子 ]として引数を渡す <form action="/boards/28/comments" accept-charset="UTF-8" method="post"> ルーティング通りネストされたリクエストが作成される
<%= form_with model:[board, comment], local: true do |f| %>
と
<%= form_with model: comment, url: [board, comment], local: true do |f| %>
前者のほうが記述がコンパクトに書けますが、後者のほうが作成更新の対象となるモデルをmodelに単一で指定しているので明確になる
モデルにインスタンスメソッドを記載する
# Userモデルに記載した場合、selfレシーバからcurrent_userを取得できるため、引数にcommentを渡してuserを取得して判定 def my_comment?(comment) self == comment.user end # if current_user.my_comment?(@comment) という形式でViewで記載 → if current_user == comment.user となる
モデルにメソッドを定義するとそのモデルの中でそのメソッドが使える
# クラスメソッドとして定義すると、実行時に判定用のオブジェクトすべてを引数として渡す必要がある。 # if User.my_comment?(current_user, @comment) # インスタンスメソッドとして定義すれば、判定メソッド実行時の引数に渡すオブジェクトが少なくて済む。 # if current_user.my_comment?(@comment)
クラスメソッドとインスタンスメソッドの対象
クラスは属性やメソッドを定義しているものなので、属性を持っているわけではない
例えばUser.emailのようなインスタンスメソッドをクラスに対して使うことはできない
上で記述している
comment.user
は、アソシエーションに酔って使えるようになったメソッド。
commentの属性user_idを使ってUserテーブルからレコードを検索する=SQLが発行される
comment.user_id
とすると、オブジェクトのuser_idを持ってくるだけ=SQLが発行されないので、負荷が減る
リファクタリング
- createアクション
def create @comment = current_user.comments.build(comment_params) @comment.board_id = params[:board_id] @board = Board.find(params[:board_id]) if @comment.save flash[:success] = t('.success') redirect_to board_path(params[:board_id]) else flash.now[:danger] = t('.fail') render 'boards/show' end end private def comment_params params.require(:comment).permit(:body) end end
createアクションはparamsから受け取った値をオブジェクトに渡して保存するだけなので、インスタンス変数として保持する必要がない。
不要なインスタンス変数は使わないように注意
class CommentsController < ApplicationController def create comment = current_user.comments.build(comment_params) if comment.save redirect_to board_path(comment.board), success: t('.success') else redirect_to board_path(comment.board), danger: t('.fail') end end private def comment_params params.require(:comment).permit(:body).merge(board_id: params[:board_id]) end end
.merge(board_id: params[:board_id])
mergeメソッドで受け取ったparamsと別のテーブルの情報をあらかじめ一緒に保存することができる
- 部分テンプレートではローカル変数を使用する
<%= render @comments, { comments: @comments } %>
のようにオプションで変更できる