#はじめに
前回、ancestryを用いて親-子のレコードを作成しました。
データ登録時にセレクトボックスで、地方(親)ごとに都道府県(子)を表示、選択できるようにしました。
#前提
前回の記事(seed作成まで)は完了していることとします。
今回はpostsテーブル/camp_sitesテーブルに、region_idというカラムを作成し、
投稿記事、キャンプサイトに地域を紐付けて保存できるようにします。
カラムの追加は完了していることとします。
#やったこと
def new
@regions = Region.where.not(ancestry: nil) #ここを追加
@post = Post.new
end
親(地方)のレコードはancestryカラムはnilとなっているので、
Region.where.not(ancestry: nil) で親ではないレコード、つまり子のレコード(都道府県)を取得しています。
<%= f.select :camp_site_id, grouped_options_for_select(@regions.map { |p| [ p.name, p.camp_sites.map { |c| [c.site_name, c.id]} ]}), prompt: "選択してください" %>
formに上記を追加します。
f.select :camp_site_id
はformの書き方通りです。
まず、@regionsには子である都道府県のレコードが格納されています(posts_controller.rbを確認)
grouped_options_for_selectは、親をひとまとまりのgroupとして扱ってくれます。
実際にコンパイルされるとこんな感じ↓↓
親(地方)が、optgroup
子(キャンプサイト)が、option
で括られているのがわかると思います。
このようにグループでまとめてくれるのが、このメソッドです(便利)。
@regions.map { |p| [ p.name, p.camp_sites.map { |c| [c.site_name, c.id]} ]}
本題のこの記述では、
@regions.map
mapメソッドで@regionsに格納されている配列データの数だけ、ブロック内で処理を行い、
最終的に配列を返してくれます。
mapメソッドの後ろについては、
|p| #ここに@regionsのレコードの一つが入ります(.eachと同様の考え方)
p.name #レコードのnameカラムが表示されます
p.camp_site.map #@regionsのレコードの一つに紐づくcamp_sites(キャンプサイト一覧)を配列データの数だけ処理を行います
|c| #camp_sitesのレコードの一つが入ります
c.site_name, c.id #camp_sitesのレコードの一つの名前を表示します。idも一緒に渡してあげます。
この処理をまとめると、まず、子である都道府県名が表示され、その後camp_site.mapの処理により、1つ1つの都道府県に関連するキャンプサイトが表示されます。
そのため、
関東地方
東京都
茨城県
中部地方
静岡県
愛知県
上記のように、子(都道府県)→関連するキャンプサイト→子(都道府県)...
と表示ができます。
考え方は同じなため、camp_sites/newについてはコードのみ記載します。
def new
@camp_site = CampSite.new
@regions = Region.where(ancestry: nil) #先ほどと違って、今回は親(地方)をregionsに入れます
end
<%= f.select :region_id, grouped_options_for_select(@regions.map { |p| [ p.name, p.children.map { |c| [c.name, c.id]} ]}), prompt: "選択してください" %>
#親(地方)と親のchildren、つまり子(都道府県)をセレクトボックスに交互に表示します。
#終わりに
ancestryを用いてのデータ操作に想像以上に苦戦しました。
一通り解決し、復習することで、だいぶ理解することができました。