地方でリモートワーク

プログラミング、先物、fx,仮想通貨なんでもやります

ネストしたリソースのformとcontroller,Rspecの作り方まとめ

スポンサーリンク

f:id:ihatov08:20160914210801j:plain

ネストしたリソースのformでつまづいたのでメモしておきます。

routes

shallowでルーティングを設定しました。 これだとnewアクションはproductに関連づけるので、productにネストした形になるのですが、他のアクションはネストする必要がないので、ネストされないルーティングが生成されます。

routes.rb
  resources :products do
    resources :product_affiliates, shallow: true
  end

controller

newとcreateはどのproductに関連づける必要があるので、productオブジェクトを取得する必要があります。また、取得したproductに関連づいたproduct_affiliateインスタンスを作る必要があります。 destroyではproductに紐付いたproduct_affiliatesにリダイレクトしたいので、productインスタンス変数にdestroyする前に格納しておく必要があります。

#product_affiliates_controller

def new
  @product = Product.find(params[:product_id])
  @product_affiliate = @product.product_affiliates.build
end

def create
  @product = @product.find(params[:product_id])
  @product_affiliate = @product.product_affiliates.build
end

def edit
  @product_affiliate = ProductAffiliate.find(prams[:id])
end

def update
  @product_affiliate = ProductAffiliate.find(params[:id])
end

def destroy
  @product_affiliate = ProductAffiliate.find(params[:id])
  @product = @product_affiliate.product
  @product_affiliate.destroy
  redirect_to product_affiliates_product_path(@product)
end

helper

肝心のviewですが、その前にhelperで定義するメソッドがあります。 newの時にはform_for [parent, child]のようにしたい、editの時にはform_for [child]のようにしたいので、下記のようなメソッドを定義します。これでnewでもeditでも動的に変化するので、1つのコードでいけます!

module ProductAffiliatesHelper
  def shallow_args(parent, child)
    child.try(:new_record?) ? [parent, child] : child
  end
end

view

viewのform_forは以下のように記述します。

<!-- _form.html.erb --!>
<%= form_for shallow_args(@product, @product_affiliate) do |f| %>

Rspec

今回はshallowを使っているので、ルーティングも考慮しなければなりません。 showページへのルートは親リソースのidと子リソースのidを指定する必要があります。

get :show, product_id: product.id, id: product_affiliate.id

newの場合は、親リソースのidのみ指定してあげればOKです。

get :new, product_id: product.id

createは親と子を指定する必要があります。どの親か判定する必要があるからです。

post :create, product_id: product.id, product_affiliate: {affiliate_type: :rakuten, affiliate_key: 'test_key', affiliate_category_id: affiliate_category.id }

editは子だけでOKです。すでにどの親に関連づいているかわかるからです。

get :edit, id: product_affiliate.id, affiliate_category_id: affiliate_category.id

updateの場合はすでにどの親かわかりますね!

patch :update, id: product_affiliate.id, product_affiliate:  @edit_attr

deleteの場合もどの親かわかるので、子だけです。

delete :destroy, id: product_affiliate.id

valiation

要件によりますが、productがあって,product_affliate子リソースがあります。productはamazon,rakutenなどのproduct_affiliateを持ちますが、productごとにamazonを二つ持ちたくないけど、product別ではamazonを持てる時はscopeでvalidationするとOKです。

validates :affiliate_type, presence: true, uniqueness: { scope: [:product_id] }

resources を nest するときは shallow を使うと幸せになれる - Qiita

ruby on rails 3 - When using shallow routes, different routes require different form_for arguments - Stack Overflow