突然ですが。Railsで「大学の生徒の履修科目を管理するシステム」を作るとします。
その生徒が履修する科目を編集する画面では、
上のように科目の数だけチェックボックスが表示され、「チェックボックスで選択した科目とその生徒を結びつけたい」とします。
そこで多くの方が抱く疑問がこちら。
くるみ
- 「モデルからチェックボックスを自動生成したい」
- 「そこから多対多の関係であるモデル達のレコードを関連付けさせたい」
という方に向けてcollection_check_boxesというビューヘルパーについて実際に使ってみてまとめました。
自分の備忘録でもありますが、ぜひ参考にしていただければ幸いです⸝⸝- ̫ -⸝⸝
お品書き
やりたいこと
このように3つのテーブルがあり、
- UserテーブルとLessonテーブルは多対多の関係
- 一人のユーザーは複数の科目を履修している
- 新しいユーザーを追加する時に、そのユーザが履修している科目(lesson)をチェックボックスで選択し送信することでそのユーザーと科目を関連づけたい
やりたいことはこんな感じ。
そんな時にcollection_check_boxesというビューヘルパーメソッドを使えばちょっと楽になります。
くるみ
collection_check_boxesの使い方
collection_check_boxesメソッドはモデルからチェックボックスを自動生成してくれる、フォーム関連のビューヘルパーです。
<%=form_for @user,url:admin_users_path do |form|%>
<%=form.collection_check_boxes(:lesson_ids, @lessons, :id, :name) do |lesson|%>
<%= lesson.check_box %>
<%= lesson.text %>
<% end %>
<%end%>
今回は上のようにform_for下で使いました。
すると無事、科目それぞれのチェックボックスが生成されました。
ブロック内でlessonという引数に対してcheckboxを呼ぶことでチェックボックスを、そしてtextを呼ぶことでそれぞれの科目名を表示しています。
ブログ主
引数について
<%=form.collection_check_boxes(:lesson_ids, @lessons, :id, :name) do |lesson|%>
引数がいっぱいあり困惑すると思うので少しだけ解説を。
引数の説明
第一引数(:lesson_ids)
→ドキュメントによるとメソッドを指定するらしい。まあparamsで取り出す時の名前ぐらいに考えればいいかも。
第二引数(@lessons)
→リレーショナルオブジェクトを指定する。ここでは全ての科目が入った@lessonsを指定している。
第三引数(:id)
→inputタグでいうvalue的なもの。ここではlessonテーブルのidカラムを指定している。
第四引数(:name)
→inputタグのテキスト的なもの。ここではlessonテーブルのnameカラムを指定している。
受け取り方
"user"=>{"student_id"=>"a", "faculty"=>"a", "grade"=>"a", "name"=>"a", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "lesson_ids"=>["", "1", "2", "3"]}
3つあるチェックボックス全てにチェックして送信した場合、パラメータは以上のようになっていました。
第一引数で指定した「lesson_ids」というキーで渡されています。
(※先頭の要素が空なのは謎…)
@lesson_ids=params[:user][:lesson_ids]
なのでこれで取り出すことができます。
ブログ主
余談
<%=collection_check_boxes(@user, :lesson_ids, @lessons, :id, :name) do |lesson|%>
第一引数にはこのように本来取り扱うモデルのオブジェクトを先頭で指定する必要があるのですが、今回のようにform_for下で記述する場合などは要らないそうです。
collection_check_boxesを使ってみた
実際にcollection_check_boxesメソッドを使って、上で述べたやりたいことをやってみました。
環境
Ruby 2.6.5
Rails 5.2.4.3
モデル
Userクラス
class User < ApplicationRecord
has_secure_password
has_many :take_lessons,dependent: :destroy
has_many :lessons,through: :take_lessons
end
Lessonクラス
class Lesson < ApplicationRecord
has_many :take_lessons,dependent: :destroy
has_many :users,through: :take_lessons
end
TakeLessonクラス
class TakeLesson < ApplicationRecord
belongs_to :user
belongs_to :lesson
end
コントローラ
users_controller
class Admin::UsersController < Admin::Base
... ... ... ...
def new
@user=User.new
@lessons=Lesson.all
end
def create
@user=User.new(user_params)
@user.admin=false
@lesson_ids=params[:user][:lesson_ids]
@lesson_ids.shift
if @user.save
@lesson_ids.each do |lesson_id|
lesson=Lesson.find(lesson_id.to_i)
@user.lessons << lesson #関連付ける
end
flash[:notice]="生徒を追加しました"
redirect_to("/admin/users")
else
flash[:notice]="生徒を追加できませんでした"
redirect_to("/admin/users/new")
end
end
... ... ... ...
def user_params
params.require(:user).permit(:name,:faculty,:grade,:student_id,:password,:password_confirmation)
end
end
params[:user][:lesson_ids]で取得できるハッシュの先頭の要素がなぜか空になるのでshiftメソッドを使ってます。
ビュー
new.html.erb
<h2>ユーザー追加</h2>
<%=form_for @user,url:admin_users_path do |form|%>
<%=form.collection_check_boxes(:lesson_ids,@lessons, :id, :name) do |lesson|%>
<ul>
<li>
<%=lesson.check_box%>
<%=lesson.text%>
</li>
</ul>
<%end%>
<%=form.submit("追加する")%>
<%end%>
結果
適当な入力で試したところ…
無事、関連付けされてました(っ´ω`c)
くるみ
結論:collection_check_boxes便利だね
以上になります。
参考になれば幸いです!では⸝⸝- ̫ -⸝⸝