ネストしたリソースの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] }
コメントを残す