Ruby_on_railsでECサイトの作成をしています。
商品詳細画面(items#show)にある、「カートに入れる」ボタンを押した時に、以下の実装をしたいです。
①ユーザーidと紐づいたカートを生成する。カートはsessionで管理する。
②cartテーブルとitemテーブルの中間テーブル的な役割を担うcart_itemテーブルに、商品の個数(quantity)と、cart_id, item_idを保存できるようにする。
現在、以下のようなエラーが表示されています。
NoMethodError in CartsController#add_item undefined method `cart_items' for nil:NilClass def setup_cart_item! @cart_item = current_cart.cart_items.find_by(item_id: params[:item_id]) end end
https://gyazo.com/eed9afc25c8418b49721673f46d6b53b
アプリ全体のER図です
https://gyazo.com/5350d2e6cdfe3d19a3e8037890ce5354
以下、該当箇所のコードになります
###ルーティング
#config/routes.rb Rails.application.routes.draw do devise_for :users root to: 'items#index' resources :items resources :carts, only: [:show] do member do post '/add_item' => 'carts#add_item' post '/update_item' => 'carts#update_item' delete '/delete_item' => 'carts#delete_item' end end end
#rails routes add_item_cart POST /carts/:id/add_item(.:format) carts#add_item update_item_cart POST /carts/:id/update_item(.:format) carts#update_item delete_item_cart DELETE /carts/:id/delete_item(.:format) carts#delete_item cart GET /carts/:id(.:format) carts#show
###アソシエーション
#app/models/user.rb class User < ApplicationRecord has_one :cart 〜略〜 end
#app/models/cart.rb class Cart < ApplicationRecord has_many :cart_items has_many :items, through: :cart_items belongs_to :user end
#app/models/item.rb class Item < ApplicationRecord has_many :cart_items end
#app/models/cart_item.rb class CartItem < ApplicationRecord belongs_to :item belongs_to :cart end
###ビュー
#app/views/items/show.html.erb(商品詳細画面) 〜略〜 <%= link_to add_item_cart_path, method: :post do %> <button class="to-cart-btn">カートに入れる</button> <% end %>
###コントローラー
#######current_cartメソッド(sessionでカート情報を保持する)の定義
#app/controllers/application_controller.rb class ApplicationController < ActionController::Base 〜略〜 protect_from_forgery with: :exception helper_method :current_cart def current_cart if session[:cart_id] @cart = Cart.find_by(id: session[:cart_id]) else @cart = Cart.create(user_id: current_user.id) session[:cart_id] = @cart.id # @cart = Cart.find_by(session[:cart_id]) @cart = Cart.find_by(id: session[:cart_id]) ★10/4 10:45 修正しました @cart end end end
###cartsコントローラー
#app/controllers/carts_controller.rb class CartsController < ApplicationController before_action :setup_cart_item!, only: [:add_item, :update_item, :delete_item] def show @cart_items = current_cart.cart_items end # 商品詳細画面から、「カートに入れる」を押した時のアクション def add_item if @cart_item.blank? @cart_item = current_cart.cart_items.build(item_id: params[:item_id]) end @cart_item.save redirect_to current_cart end # カート詳細画面から、「更新」を押した時のアクション def update_item @cart_item.update(quantity: params[:quantity].to_i) redirect_to current_cart end # カート詳細画面から、「削除」を押した時のアクション def delete_item @cart_item.destroy redirect_to current_cart end private def setup_cart_item! @cart_item = current_cart.cart_items.find_by(item_id: params[:item_id]) end end
####一度cart_itemsテーブル共にidが入った時があります。
ルーティングを以下のようにした時です。
resources :items do resources :carts post '/add_item' => 'carts#add_item' post '/update_item' => 'carts#update_item' delete '/delete_item' => 'carts#delete_item' end
#rails routes item_add_item POST /items/:item_id/add_item(.:format) carts#add_item 〜中略〜 new_item_cart GET /items/:item_id/carts/new(.:format) carts#new
この場合、以下のようなエラーが出ます
NoMethodError in CartsController#add_item undefined method `cart_url' for #<CartsController:0x00007fa1e4688ed0> @cart_item.save redirect_to current_cart end
https://gyazo.com/14affbd26e5ed72bfc393da1a015ce96
###見解
cart_itemsに値が入ったのは良いが、ルーティングでcartをitemにネストするのはおかしいのではないかと思い、ネストを外すことにしましたが、それですとcart_itemsに値が入らなくなってしまいます。
ルーティングの変更やbinding.pryを用いたparamsの確認など行いましたが、うまくいきません。
なかなか解決できず本当に困っております。
長文で読みにくくて申し訳ありませんが、お力添えいただけると幸いです。
アドバイスをよろしくお願い申し上げます。
###10/4 11:00 追記
hatsu様にコメントをいただき、様々な箇所でbinding.pryを実行してみました。コメント欄では見辛かったので、本文に追記いたしました。
まず、current_cartのsession[:cart_id]と、Cartが存在するか確かめました。
8: def current_cart => 9: binding.pry 10: if session[:cart_id] 11: @cart = Cart.find_by(id: session[:cart_id]) 12: else 13: @cart = Cart.create(user_id: current_user.id) 14: session[:cart_id] = @cart.id 15: @cart = Cart.find_by(id: session[:cart_id]) 16: @cart 17: end 18: end [2] pry(#<CartsController>)> session[:cart_id] => 13 [4] pry(#<CartsController>)> Cart.find_by(id: session[:cart_id]) Cart Load (0.5ms) SELECT `carts`.* FROM `carts` WHERE `carts`.`id` = 13 LIMIT 1 ↳ (pry):43:in `current_cart' => #<Cart:0x00007fa1d1e9e1f0 id: 13, user_id: 4,
と、session[:cart_id]と、そのCartは存在するようです。
set_up_item!の直後にもbinding.pryを置いてみました
34: def setup_cart_item! => 35: binding.pry 36: @cart_item = current_cart.cart_items.find_by(item_id: params[:item_id]) 37: end [1] pry(#<CartsController>)> current_cart Cart Load (0.5ms) SELECT `carts`.* FROM `carts` WHERE `carts`.`id` = 13 LIMIT 1 ↳ app/controllers/application_controller.rb:10:in `current_cart' => #<Cart:0x00007fa1d1a4db50 id: 13, user_id: 4, created_at: Sun, 04 Oct 2020 00:26:44 JST +09:00, updated_at: Sun, 04 Oct 2020 00:26:44 JST +09:00> [3] pry(#<CartsController>)> cart_items NameError: undefined local variable or method `cart_items' for #<CartsController:0x00007fa1d1886858> Did you mean? cart_path
こちらで、current_cartは取得できるようになっていることがわかりました。
しかし、[3] pry(#<CartsController>)> cart_items
では再び、 undefined local variable or method `cart_items' となり、cart_itemsが定義されていないと出てしまいます。
同一の箇所にbinding.pryをかけ、質問を変えてみました
34: def setup_cart_item! => 35: binding.pry 36: @cart_item = current_cart.cart_items.find_by(item_id: params[:item_id]) 37: end [1] pry(#<CartsController>)> current_cart.cart_items Cart Load (0.6ms) SELECT `carts`.* FROM `carts` WHERE `carts`.`id` = 13 LIMIT 1 ↳ app/controllers/application_controller.rb:10:in `current_cart' CartItem Load (0.4ms) SELECT `cart_items`.* FROM `cart_items` WHERE `cart_items`.`cart_id` = 13 ↳ app/controllers/carts_controller.rb:35:in `setup_cart_item!' => [#<CartItem:0x00007fa1d1eccde8 id: 15, quantity: 0, item_id: 29, cart_id: 13, created_at: Sun, 04 Oct 2020 02:25:43 JST +09:00, updated_at: Sun, 04 Oct 2020 02:25:43 JST +09:00>] [2] pry(#<CartsController>)> current_cart.cart_items.find_by(item_id: params[:item_id]) CACHE Cart Load (0.0ms) SELECT `carts`.* FROM `carts` WHERE `carts`.`id` = 13 LIMIT 1 [["id", 13], ["LIMIT", 1]] ↳ app/controllers/application_controller.rb:10:in `current_cart' CartItem Load (0.5ms) SELECT `cart_items`.* FROM `cart_items` WHERE `cart_items`.`cart_id` = 13 AND `cart_items`.`item_id` IS NULL LIMIT 1 ↳ (pry):55:in `setup_cart_item!' => nil
[1] pry(#<CartsController>)> current_cart.cart_items
ではcart_itemsの中身も取得できているように見えるのですが、
[2] pry(#<CartsController>)> current_cart.cart_items.find_by(item_id: params[:item_id])
とすると、nilになってしまいます。
def setup_cart_item! @cart_item = current_cart.cart_items end
と.find_by(item_id: params[:item_id]を抜いてみた方が良いのでしょうか。
試しに抜いてみて、add_itemメソッドの直下にbinding.pryを仕掛けますと、
10: def add_item => 11: binding.pry 12: if @cart_item.blank? 13: @cart_item = current_cart.cart_items.build(item_id: params[:item_id]) 14: end 15: @cart_item.save 16: redirect_to current_cart 17: end [1] pry(#<CartsController>)> @cart_item CartItem Load (0.5ms) SELECT `cart_items`.* FROM `cart_items` WHERE `cart_items`.`cart_id` = 13 ↳ app/controllers/carts_controller.rb:11:in `add_item' => [#<CartItem:0x00007fa1d00d4548 id: 15, quantity: 0, item_id: 29, cart_id: 13, created_at: Sun, 04 Oct 2020 02:25:43 JST +09:00, updated_at: Sun, 04 Oct 2020 02:25:43 JST +09:00>]
となり、set_up_item!メソッドは実行できていると考えます。しかし、この際、
NoMethodError in CartsController#add_item undefined method `save' for #<CartItem::ActiveRecord_Associations_CollectionProxy:0x00007fa1eebacee8> Extracted source (around line #16): end @cart_item.save redirect_to current_cart end
saveできないようになってしまいます。saveメソッドが使えないということはどういうことなのでしょうか?ご回答いただけると幸いです。
お忙しいところ恐縮ですが、よろしくお願い申し上げます。
回答1件
あなたの回答
tips
プレビュー