めもめも のーと

ハマったこととか、覚えたこととか

collection_check_boxesで登録済みの値にチェックをつけて表示する方法を調べた

Railsで多対多のリレーションのモデルをチェックボックスで表現する際にcollection_check_boxes と言うViewヘルパー関数を使うと便利。 しかし編集時など、登録した値は当然チェックがついた状態で表示されないと困るが、どういう仕組みかの説明が見当たらなかったので調べた。

前提

下記のようなリレーションとなっていることを前提とする。

◆リレーション
post <--> author_post <--> author

◆model定義
class Post < ApplicationRecord
  has_many :author_posts
  has_many :authors, through: author_posts
...

class Author < ApplicationRecord
  has_many :author_posts
  has_many :posts, through: :author_posts
...

class AuthorPost < ApplicationRecord
  belongs_to :post
  belongs_to :author

viewにcollection_check_boxesを下記のように設置

= collection_check_boxes(:post, :author_ids, Author.all, :id, :name) do |b|
  = b.label { b.check_box + b.text }

formオブジェクトを使う場合
= f.collection_check_boxes(:author_ids, Author.all, :id, :name) do |b|
  = b.label { b.check_box + b.text }

  # ※viewにAuthor.allを直接記述するのはよくない

:id:nameは下記のように展開される

<input type="checkbox" value="4" checked="checked" name="post[author_ids][]" id="post_author_ids_4">

value : authorのID
id : post_{第2引数の文字列}_{authorのID}

各項目の意味

各引数の役割はそれぞれ下記の通り

:post 更新対象のオブジェクト(のシンボル名)。form_forでは省略可。

:author_ids 更新対象のオブジェクトが持つメソッド(のシンボル名)。チェックボックスの各チェックに関連付けられるID。登録や更新の際にparameterに含まれるので、StrongParameterの対応が必要。

# app/controller/posts_controller.rb
def post_params
  params.require(:post).permit(:title, {  author_ids: [] })
end

# ここで定義しないと「Unpermitted parameter: author_ids」となって登録や更新がされない。

:id chekcboxのinputタグにてid属性に割り振られる値。かつ、登録済みの値かどうかを比較する対象となる属性(後述)。authorが持つメソッドを指定。

:name チェックボックスに表示されるinputタグのテキストとなる値。authorが持つメソッドを指定。

# 例えばauthorが下記メソッドを持つ場合、:nameのところで代替可能
def honorific
  name + ''
end

まとめ

今回分かってよかったのが、第2、4引数(form_forの場合は第1、3引数)であるauthor_ids:idの関係。

post(正確には第1引数のオブジェクト)はauthor_idsというメソッドを持っており、その結果と第4引数idを比較し、合致する場合にチェックボックスにチェックが付いた状態で画面表示される。(第4引数のidは、第3引数のそれぞれのオブジェクトが持つid。今回ではAuthor.allのそれぞれのauthorのauthor.id)

例えばpostに紐付くauthorのidが1,3,4の場合、post.author_ids = [1,3,4]となり、 Author.allの各authorのidと比較し、合致する場合にcheck属性が付与される。

上記を理解しておけば、登録した値が編集画面でチェックされていない、という場合にも原因の切り分けがしやすくなるので、覚えておこう。