質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

解決済

1回答

957閲覧

Ruby on rails ECサイトのエラーを解決したい NoMethodError in CartsController#add_item

miitaka

総合スコア2

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2020/10/03 17:36

編集2020/10/04 01:58

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メソッドが使えないということはどういうことなのでしょうか?ご回答いただけると幸いです。
お忙しいところ恐縮ですが、よろしくお願い申し上げます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hatsu

2020/10/03 23:31

undefined method `cart_items' とのことなので、current_cartが取得できていないと思われます。 current_cartのelseで@cart = Cart.find_by(session[:cart_id])となっていますが、これはCart.find_by(id: session[:cart_id])と書く必要があります。が、ここでエラーになっていないので、if文のif session[:cart_idの中身が走っていそうです。 検証するためにdef current_cartのすぐ下にbinding.pryを入れてsession[:cart_id]に取得したいカートのIDが入っているか、そのCartは存在するかを確認するといかがでしょうか。
miitaka

2020/10/04 01:50 編集

hatsu様 回答いただきありがとうございます!ご指摘いただいた箇所を修正させていただき、binding.pryを実行しました。以下、長くなってしまいましたがお付き合いいただけると幸いです。 ``` 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メソッドが使えないということはどういうことなのでしょうか?ご回答いただけると幸いです。 お忙しいところ恐縮ですが、よろしくお願い申し上げます。
hatsu

2020/10/04 11:21

> current_cart.cart_items.find_by(item_id: params[:item_id]) とすると、nilになってしまいます。 こちらは、add_itemのRoutesをみると、/carts/:id/add_item なので、params[:item_id]は存在せず、params[:id]とする必要があるのかもしれないです。
miitaka

2020/10/04 12:22 編集

hatsu様 ご回答ありがとうございます! ご報告ですが、解決することができました。 ルーティングのネストを ``` resources :carts, only: [:show] resources :items do post '/add_item' => 'carts#add_item' post '/update_item' => 'carts#update_item' delete '/delete_item' => 'carts#delete_item' end ``` と、add_itemらをitemにネストすることで無事cart_itemsテーブルに値を保存できました。ずっとcartと関連づけることを考えてしまっていましたが、よくよく考えば、「どのitemをcart_itemに入れるか?」なので、itemにネストするのが正解だなと思いました。 何が正解かわからない中、hatsu様がコメントを下さったことはかなり心強かったです。本当にありがとうございます。 まだitemの数量(quantity)の設定の実装などはできていませんが、ひとまず解決とさせていただきます。
guest

回答1

0

自己解決

ルーティングのネストを

resources :carts, only: [:show] resources :items do  post '/add_item' => 'carts#add_item'  post '/update_item' => 'carts#update_item'  delete '/delete_item' => 'carts#delete_item' end

と、add_itemらをitemにネストすることでcart_itemsテーブルに値を保存できた。

投稿2020/10/04 12:25

miitaka

総合スコア2

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問