実現したいこと
同じテーブル同士で1つのに対して、複数の同じテーブルのレコードを紐づけてチェックボックスで登録できるようにする。
例えば、
- ユーザーの一括お気に入り機能
- 一括ブロック機能
- アイテムをひとまとめにするアイテム
などが挙げられます。
記事にした経緯として、
- 探しても実装事例が全く見つからなかった
- この発想に至るまでハマって時間を無駄にした
というものからです。
開発環境
- Ruby 3.0.2
- Rails 7.0.3.1
オーソドックスな管理方法
よくある例として車とパーツの関係でテーブル設計をしてみました。
利用例としては、車に対して装着したいカスタムパーツにチェックを入れる想定です。
※ストロングパラメーターの設定は、実装例が多くあるため割愛しております。
テーブル設計
cars
カラム名 | 型 |
---|---|
id | integer |
name | string |
car_parts
カラム名 | 型 |
---|---|
car | reference |
part | reference |
parts
カラム名 | 型 |
---|---|
id | integer |
name | string |
- carモデル
class Car < ApplicationRecord
has_many :car_parts, dependent: :destroy
has_many :parts, through: :car_parts
accepts_nested_attributes_for :car_parts, allow_destroy: true
end
- 中間テーブルとなるcar_partモデル
class CarPart < ApplicationRecord
belongs_to :car
belongs_to :part
end
- partモデル
class Part < ApplicationRecord
has_many :car_parts, dependent: :destroy
has_many :cars, through: :car_parts
accepts_nested_attributes_for :car_parts, allow_destroy: true
end
チェックボックスとして表示させるフォーム
= form_with model: @car, local: true do |f|
= f.fields_for :car_parts do |l|
= l.collection_check_boxes :part_ids, Part.all, :id, :style, checked: @car.part_ids ,include_hidden: false do |b|
label { b.check_box + b.text }
= f.submit
本題
同じテーブル間でチェックを入れて追加し合う場合
アイテムをひとまとめにする機能を例に挙げてみます。
テーブル設計
items
カラム名 | 型 |
---|---|
id | integer |
name | string |
item_relations
カラム名 | 型 |
---|---|
item | reference |
item_selection | reference |
item_selection(クラス継承)
理解しやすくするための便宜上、テーブル設計に挙げています。
カラム名 | 型 |
---|---|
Itemテーブルと同じ | Itemテーブルと同じ |
itemモデル
item.rb
class Item < ApplicationRecord
has_many :item_relations, dependent: :destroy
has_many :item_selections, through: :item_relations
accepts_nested_attributes_for :item_relations, allow_destroy: true
end
- 中間テーブルとなるitem_relationモデル
item_relation.rb
class ItemRelation < ApplicationRecord
belongs_to :item
belongs_to :item_selection, class_name: 'Item'
end
単一テーブル継承(STI)を使って
Itemクラスを継承したItemSelectionクラスを用意
item_selection.rb
class ItemSelection < Item
has_many :item_relations, dependent: :destroy
has_many :items, through: :item_relations
accepts_nested_attributes_for :item_relations, allow_destroy: true
end
チェックボックスとして表示させるフォーム
= form_with model: @item, local: true do |f|
= f.fields_for :item_selections do |l|
= l.collection_check_boxes :item_selection_ids, ItemSelection.all, :id, :style, checked: @item.item_selection_ids ,include_hidden: false do |b|
label { b.check_box + b.text }
= f.submit
コンパクトに実装ができました🎉
参考記事
非常にわかりやすい構成で執筆の際に参考にさせていただきましたmm