Ruby on Railsでチェックボックス(複数選択)を作る
今日は複数選択可能なチェックボックスをどうやって作るか、どうデータベースに保存するのかについてまとめます。
チェックボックスとは?
チェックボックスはユーザーがクリックすることで項目のオン/オフが出来るボックスです。
「利用規約に同意する はい/いいえ」のようなユーザーの意思を確認するような用途や、アンケートフォームのように、複数の選択肢から任意の0個以上の選択をさせる場合などに使われます。
今回は、複数選択させたい場合の実装方法について書き記します。
チェックボックスの実装の下準備
複数選択させたい実装方法の場合は、選択肢を保存するテーブルと中間テーブルの作成が必要となります。
以前作成したプロフィールを登録するアプリに趣味(hobby)を複数登録できるよう追加で実装していきます。
テーブル構成
profilesテーブル
Column | Type | Options |
---|---|---|
name | string | null: false |
sex | string | null: false |
- has_and_belongs_to_many :hobbies
hobbiesテーブル
Column | Type | Options |
---|---|---|
category | string |
- has_and_belongs_to_many :profiles
hobbies_profilesテーブル (中間テーブル)
Column | Type | Options |
---|---|---|
profile_id | references | null: false, foreign_key: true |
hobby_id | references | null: false, foreign_key: true |
今回は中間テーブル用のモデルを作らず、has_and_belongs_to_manyを使用してアソシエーションを組んでいます。詳しくは下記リンク参照↓
hobbiesテーブル(選択肢用のテーブル)にレコードを保存
選択肢を作成します。こんな感じ。
チェックボックスの実装
チェックボックスをビューで作成するには、フォームヘルパーのcollection_check_boxesを使用します。
<%= form.collection_check_boxes :hobby_ids, Hobby.all, :id, :category %>
collection_check_boxesの1つめのパラメーターを:hobby_idsとしました。これはProfileを保存する際に、中間テーブルのhobby_idにも情報を保存するためです。
2つめのパラメータHobby.allはhobbiesテーブルに保存されている全レコードを意味します。
詳しくはこちら↓
ビューの表示とHTML出力は以下のようになります。
<input type="hidden" name="profile[hobby_ids][]" value="">
<input type="checkbox" value="1" name="profile[hobby_ids][]" id="profile_hobby_ids_1">
<label for="profile_hobby_ids_1">スポーツ</label>
<input type="checkbox" value="2" name="profile[hobby_ids][]" id="profile_hobby_ids_2">
<label for="profile_hobby_ids_2">読書</label>
~ 以下 {id: 9, category: "その他"} まで同様に作成 ~
それでは、趣味の項目2つ(1.スポーツ、3.映画)を選んでProfileを保存してみます。
このとき、paramsは下記のようになります。
{"authenticity_token"=>"wr9ZnNuKV5XZ9rten3ODhSA8r/rmqcMI0D/eB5J6o+m73NoOAmQXY9+P5IloYk3Lb4juPrcEZkJ5eRr3uTaIew==",
"profile"=>{"name"=>"さとし", "sex"=>"male", "hobby_ids"=>["", "1", "3"]}, "commit"=>"SEND", "controller"=>"profiles", "action"=>"create"}
ハッシュのキーhobby_idsの値が配列["", "1", "3"]になっていますので、ストロングパラメーターで配列を受け取れるようにしてあげます。
def create
@profile = Profile.new(profile_params)
@profile.save
redirect_to root_path
end
private
def profile_params
params.require(:profile).permit(:name, :sex, hobby_ids:[])
end
レコード保存の記述はいつも通りの感じでOK。アソシエーションを組んでいるおかげで、Profileの保存と同時に中間テーブルにもレコードが保存されます。
選択肢用テーブルをActiveHashで代用できないか?
中間テーブルつくるのめんどくさいので選択肢用のテーブルは変更があまりないので、ActiveHashで代用できないかなと色々試行錯誤してみましたが、どうやらActiveHashは多対多のアソシエーションは組むことができないようです。
メソッドを追記したりすればいけるみたいなのですが、使いこなせる自信があまりなかったので正攻法(中間テーブル作成)をご紹介しました。
ActiveHashの使い方や、ActiveHashで多対多を実現するための記述は下記リンクがわかりやすくておすすめです。
Discussion