ネストしたリソースの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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です