概要
- 親子でネストされたリソースがあるとき、子のレコードを作成、編集する時のコントローラは、親子のコントローラと子のコントローラそれぞれ作った方がよさそう。
背景
親子の関係を有するモデルがある時、親のviewで子のデータだけを更新したいということありますよね。例えば、団体が主催するイベントを管理するアプリがあるとき、団体のページからイベントを管理(作成・編集)したいし、イベント独自の詳細ページでも管理もしたい、という場合です。
関係をmodelとviewで表すと以下のようなかんじです。
class Organization < ActiveRecord
has_many :events
団体のページにあるイベントのフォーム
= @organization.name
// ここにeventのフォームを追加する
イベント独自の詳細ページのフォーム
= form_with model: @event
// ここにeventのフォームを追加する
ここで問題となるのは、どのページから遷移してきたかによってリダイレクト先をコントローラーで判断させなくてはならないことです。
具体的には、団体のページから遷移してきた場合は、フォームを更新後、団体のページに戻したい。一方、イベントのページから遷移してきた場合はイベントのページに戻したい。
もし、これを events_controller.rb
の条件分岐で頑張るとしたらいかのようになります。
def update
@event = Event.find(params[:id])
@event.assign_attributes(event_params)
if @event.valid?
if request.referer == organization_url(@organization)
redirect_to @organization
else
redirect_to @event
end
end
request.referer == organization_url(@organization)
の条件分岐がそれです。うーん、・・・なんか見るからにつらそう。
これをすっきり書きたいというのが今回のモチベーションです。
結論
- ネストされたendpoint (
/organizations/:organization_id/events/:id
) と 団体ページからのフォーム用のコントローラorganization_events_controller
と を用意する。
ネストされたendpoint
まず、ネストされたendpointを routes.rb
で用意します。
Rails.application.routes.draw do
resources :organizations do
resources :events, controller: 'organization_events'
end
resources :events
end
, controller: 'organization_events'
のようにリソースにアクセスしたときのコントローラの向き先を団体ページからのフォームから遷移した場合のコントローラをつくるところがポイント。
(これがないと、 events_controller.rb
に遷移してしまいます!)
団体ページからのフォーム用のコントローラ
上記で追加した、ネストされたendpointに基づいたフォーム用のコントローラは以下のようにかきます。
def update
@event = Event.find(params[:id])
@event.assign_attributes(event_params)
if @event.valid?
redirect_to @organization
end
end
なお、 結果的にevent_controller.rb
も条件分岐がなくなり、以下のようにすっきりします。
def update
@event = Event.find(params[:id])
@event.assign_attributes(event_params)
if @event.valid?
redirect_to @event
end
end
ちなみに、団体ページでイベントデータを編集する場合は以下の通りです。 model: [:organization, @event]
polymorphic_path をつかうところがポイント・・・かな。
= @organization.name
= form_with model: [:organization, @event] do |f|
= f.text_field :name
利点、欠点
- 利点
- コントローラがすっきりする
- 欠点
- 記述が重複する。たとえば、event_params (event用の
params.require(:event).permit(:name)
など )は重複します。
- 記述が重複する。たとえば、event_params (event用の
個人的には、cotntrollerの記述は、ロジックが単純なら重複しても良いと思うので、欠点は気にならなかったので、コントローラがすっきりする利点をとりました。
以上。