ヨージとプログラミング

プログラミング勉強の記録

【Rails5】コメント機能でのStrongParameterの設定方法

ハッシュが2つ出てくる

私が今回実装したコメント投稿formはこんな感じ。

= form_with(model: [@post, @comment]) do |f|
  .form-group
    = f.text_area :body, placeholder: 'コメント追加.', class:'form-control'
    = f.submit '投稿する', class:'btn'

一見普通ですがいつもと違うのはmodel: [@post, @comment]の部分です。普段であれば、渡すmodelは1つですが、今回は関連元、関連先の2つのインスタンスを指定します。
これによって、POSTしたときに渡されるパラメーターが以下のようになります。

Parameters: {
   "utf8"=>"XXX", 
   "authenticity_token"=>"XXXXXX", 
   "comment"=>{"body"=>"はじめまして!"}, 
   "commit"=>"XXXXX", 
   "post_id"=>"8"
}

commentテーブルに保存するデータは、"comment"=>{"body"=>"はじめまして!"}"はじめまして!"で、params.require(:comment).permit(:body)でストロングパラメーターを作ればいいじゃないかと思うかもしれません。しかし、commentsテーブルには:post_idのカラムもあるため、 "post_id"=>"8"の保存も必要です。

単純な発想ではArgumentErrorになる

まず思いつくのがこのような形かもしれません。これはエラーです。

params.require(:comment, :post_id).permit(:body)

では:post_idをpermitの方に入れてしまうのはどうでしょう? これももちろんエラーです。:post_idは:commentの値ではありませんので。

params.require(:comment).permit(:body, :post_id)

そもそもrequire, permitとは何か

こちらの記事 RailsのStrong Parametersを調べる - Qiita で紹介されていますが、requireは以下のように定義されています。

def require(key)
  self[key].presence || raise(ActionController::ParameterMissing.new(key))
end

self[key]の 部分は、アクセプター(params)の中に引数で渡したkeyのvalueが存在しているとtrueを返すようです。
他の記事ではrequire()の引数を2つ以上にしていてもいけるようなことが書かれていましたが、今回の場合はエラーとなりました(なぜだ)

引数を2つ指定
params.require(:comment, :post_id)

=>ArgumentError (wrong number of arguments (given 2, expected 1)):

次にpermit()ですが

def permit(*filters)
  params = self.class.new

  filters.flatten.each do |filter|
    case filter
    when Symbol, String
      permitted_scalar_filter(params, filter)
    when Hash then
      hash_filter(params, filter)
    end
  end

  unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters

  params.permit!
end

これははっきりと引数を複数とれることが明記されていますね。 処理も単純で渡されたパラメーターを許可するようです。

個別にStrongParameterして連結する

解決方法ですが、合わせてStrongParameterを指定することが困難でしたので、:commnetと:post_idで別個に指定し、後でmerge()でハッシュを連結しました。

class CommentsController < ApplicationController
  def create
    comment = current_user.comments.build(comment_params)
    comment.save
    〜
  end

  private
  def comment_params
    params.require(:comment).permit(:body).merge(params.permit(:post_id))
  end
end

これで一応うまくいきました。
きっともっとクールな方法があるんでしょうが思いつきませんでした。 知っている方ぜひ教えて下さい!