チワワかわいいブログ

RUNTEQでrails勉強する日々の記録

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 } %>

のようにオプションで変更できる