0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ネストされた子モデル用のコントローラを作ることで子のコントローラをすっきりさせる

Last updated at Posted at 2019-05-05

概要

  • 親子でネストされたリソースがあるとき、子のレコードを作成、編集する時のコントローラは、親子のコントローラと子のコントローラそれぞれ作った方がよさそう。

背景

親子の関係を有するモデルがある時、親のviewで子のデータだけを更新したいということありますよね。例えば、団体が主催するイベントを管理するアプリがあるとき、団体のページからイベントを管理(作成・編集)したいし、イベント独自の詳細ページでも管理もしたい、という場合です。

関係をmodelとviewで表すと以下のようなかんじです。

organization.rb
class Organization < ActiveRecord
  has_many :events

団体のページにあるイベントのフォーム

views/organizations/show.html.slim
= @organization.name
// ここにeventのフォームを追加する

イベント独自の詳細ページのフォーム

views/events/edit.html.slim
= form_with model: @event
// ここにeventのフォームを追加する

ここで問題となるのは、どのページから遷移してきたかによってリダイレクト先をコントローラーで判断させなくてはならないことです。
具体的には、団体のページから遷移してきた場合は、フォームを更新後、団体のページに戻したい。一方、イベントのページから遷移してきた場合はイベントのページに戻したい。
もし、これを events_controller.rb の条件分岐で頑張るとしたらいかのようになります。

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 で用意します。

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に基づいたフォーム用のコントローラは以下のようにかきます。

controllers/organization_events_controller.rb
def update
  @event = Event.find(params[:id])
  @event.assign_attributes(event_params)
  if @event.valid?
    redirect_to @organization
  end
end

なお、 結果的にevent_controller.rb も条件分岐がなくなり、以下のようにすっきりします。

controllers/events_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 をつかうところがポイント・・・かな。

views/organizations/show.html.slim
= @organization.name
= form_with model: [:organization, @event] do |f|
  = f.text_field :name 

利点、欠点

  • 利点
    • コントローラがすっきりする
  • 欠点
    • 記述が重複する。たとえば、event_params (event用の params.require(:event).permit(:name) など )は重複します。

個人的には、cotntrollerの記述は、ロジックが単純なら重複しても良いと思うので、欠点は気にならなかったので、コントローラがすっきりする利点をとりました。

以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?