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

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

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

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

Q&A

解決済

2回答

2052閲覧

RailsでECサイトを作成中、商品をカートに追加しようとするとNoMethodError in CartsController#add_itemのエラーが出る

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby on Rails

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

0グッド

1クリップ

投稿2020/07/05 04:02

編集2020/07/05 16:12

前提・実現したいこと

現在、プログラミングを学習中の初学者です。
ポートフォリオ制作の一環でrailsでカート機能を備えたECサイトを作成しています。

ログイン状態でトップページに設定している、products#indexの「カートに入れる」ボタンを押すと、ユーザーidと紐づいたカートが生成され、cartテーブルとproductテーブルの中間テーブル的な役割を担うcart_itemテーブルに商品の個数と、それぞれのidを保存できるようにしたいです。
現在、「カートに入れる」ボタンを押すと、下記のエラーが吐かれてしまいます。

該当のフォームやルーティングに問題があるのでは無いかと思いますが、いろいろ調べてもどうしてよいか分かりません。
何日も解決できず困っています。アドバイスをいただけると幸いです。
申し訳ございませんがよろしくお願いいたします。

下記、エラーメッセージとソースコードです。

発生している問題・エラーメッセージ

イメージ説明

該当のソースコード

Ruby

1#routes.rb 2root 'products#index' 3 4 resources :products, only: [:index, :new, :create, :edit, :update] do 5 resources :carts, only: [:create, :new] 6 member do 7 post '/add_item' => 'carts#add_item' 8 post '/update_item' => 'carts#update_item' 9 delete '/delete_item' => 'carts#delete_item' 10 end 11 end 12 13 resources :users, only: [:show,:edit,:update] 14 15 resources :carts, only: [:show,:create,:new] 16

Haml

1#app/views/products/index.html.haml 2#トップページと商品の購入ページを兼ねています。 3 4 %ul 5 - @products.each do |product| 6 =form_for([@product, @cart_item], url: add_item_product_path(product), method: :post) do |f| 7 %li.product 8 .pro-main 9 .pro-left 10 .pro-image 11 = image_tag product.image.url, height: "120" 12 .pro-name 13 = product.name 14 .pro-center 15 .pro-explain 16 = product.explain 17 .pro-right 18 .pro-price 19 = product.price 20 %span 2122 =f.hidden_field :product_id, value: product.id 23 .pro-qua 24 = f.number_field :quantity, value: "0", style: "text-align:right", min: 0, class: "pro-quantity" 25 .pro-cart 26 = f.submit "カートに入れる", class: "cart-in", name: "add_cart"

Ruby

1#application_controller.rb 2#current_cartを定義しています 3 helper_method :current_cart 4 5 def current_cart 6 @cart = Cart.find_by(id: session[:cart_id]) || Cart.create 7 session[:cart_id] = @cart.id 8 @cart 9 end
#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 @cart = current_cart @product =Product.find(params[:id]) @cart_items = @cart.cart_items.build if @cart_item.blank? @cart_item = current_cart.cart_items.build(product_id: params[:id], cart_id: current_cart.id) end @cart_items.quantity += params[:quantity].to_i @cart_items.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 cart_params params.require(:cart).permit(cart_items: [:id, :product_id, :cart_id]) end def setup_cart_item! @cart_item = current_cart.cart_items.find_by(product_id: params[:product_id]) end end

Ruby

1#products_controller.rb 2class ProductsController < ApplicationController 3 4 def index 5 @products = Product.all 6 @cart_item = CartItem.new 7 end 8 9 def new 10 @product = Product.new 11 end 12 13 def create 14 @product = Product.new(product_params) 15 if @product.save 16 redirect_to root_path 17 else 18 render @product.current_cart 19 end 20 end 21 22 private 23 def product_params 24 params.require(:product).permit(:name, :explain, :image, :price) 25 end 26 27end

Ruby

1#cart.rb 2class Cart < ApplicationRecord 3 has_many :cart_items, dependent: :destroy 4 has_many :products, through: :cart_items 5 belongs_to :user 6end

Ruby

1#cart_item.rb 2class CartItem < ApplicationRecord 3 belongs_to :product 4 belongs_to :cart 5end

Ruby

1#product.rb 2class Product < ApplicationRecord 3 mount_uploader :image, ImageUploader 4 has_many :cart_items, dependent: :destroy 5 has_many :cart, through: :cart_items 6end

Ruby

1#user.rb 2class User < ApplicationRecord 3 has_one :cart, dependent: :destroy 4end
Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) users/sessions#new user_session POST /users/sign_in(.:format) users/sessions#create destroy_user_session DELETE /users/sign_out(.:format) users/sessions#destroy new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit user_password PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update POST /users/password(.:format) devise/passwords#create cancel_user_registration GET /users/cancel(.:format) users/registrations#cancel new_user_registration GET /users/sign_up(.:format) users/registrations#new edit_user_registration GET /users/edit(.:format) users/registrations#edit user_registration PATCH /users(.:format) users/registrations#update PUT /users(.:format) users/registrations#update DELETE /users(.:format) users/registrations#destroy POST /users(.:format) users/registrations#create sign_in GET /sign_in(.:format) users/sessions#new sign_out GET /sign_out(.:format) users/sessions#destroy root GET / products#index product_carts POST /products/:product_id/carts(.:format) carts#create new_product_cart GET /products/:product_id/carts/new(.:format) carts#new product_cart GET /products/:product_id/carts/:id(.:format) carts#show add_item_product POST /products/:id/add_item(.:format) carts#add_item update_item_product POST /products/:id/update_item(.:format) carts#update_item delete_item_product DELETE /products/:id/delete_item(.:format) carts#delete_item products GET /products(.:format) products#index POST /products(.:format) products#create new_product GET /products/new(.:format) products#new edit_product GET /products/:id/edit(.:format) products#edit product PATCH /products/:id(.:format) products#update PUT /products/:id(.:format) products#update edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PATCH /users/:id(.:format) users#update PUT /users/:id(.:format) users#update

試したこと

下記の記述等を参考にしましたがうまくいきませんでした。

rails カート機能の実装(多対多)NoMethodError
RailsでEC系に良く出てくるカート機能を解説 / 実装してみた
Rails5でカート機能を作るためのロジックを作ってみた

ルーティングや、コントローラーの設定、フォームの使い方などに関する理解の浅さこそが最大の原因であると思いますが、どうにも進めなくなってしまい、お力添えをお願いしたいです。

補足情報(FW/ツールのバージョンなど)

Rails 5.0.7.2

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

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

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

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

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

guest

回答2

0

現状だとparams[:product_id]は存在しないと思うので、findで取得できない状態なんだと思います。
params[:product_id]を利用したいのであれば、routesのcollectionをmemberに変えてみてください。
パスの方はadd_item_products_path(product_id)みたいな感じになると思います。

routes.rb 該当部分のみ

Ruby

1 member do 2 post '/add_item' => 'carts#add_item' 3 post '/update_item' => 'carts#update_item' 4 delete '/delete_item' => 'carts#delete_item' 5 end

今のルーティングを維持するなら、
コントローラーで利用しているparams[:product_id] が cart_paramsが正しく動作してるとすると、 params[:cart][:cart_item][:product_id] になるかと思います。

参考になればと思います。

投稿2020/07/05 08:03

Cojiro

総合スコア539

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

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

退会済みユーザー

退会済みユーザー

2020/07/05 08:24

ご回答いただきありがとうございます collectionをmemberに変更したところ、 ActionController::UrlGenerationError in Products#index No route matches {:action=>"add_item", :controller=>"carts"} missing required keys: [:id] のエラーが出ました。これはご指摘いただいている通り、cart_paramsが動作していないということでしょうか?
Cojiro

2020/07/05 09:29

おそらく下記の箇所でエラーが出てるかと思いますので、cart_paramsとは関係ないです。 ルーティングを変更したので、urlの指定の方法を変更する必要があります。 =form_for([@product, @cart_item], url: add_item_product_path, method: :post) do |f| のadd_item_product_pathをadd_item_product_path(product) とすればいいと思います。 ※productは、商品情報(Product)の変数です。 もしくは、 今のルーティングにおいては、 @productに商品情報が入っていれば、(Product.newではなく、Product.find(1)のように情報が入っていること) winterboumさんのおっしゃるように、 =form_for([@product, @cart_item]) do |f| でも、エラーなく表示されると思います。
退会済みユーザー

退会済みユーザー

2020/07/05 12:34

ご回答いただきありがとうございます。 ご指摘の通りにformのpathを変更し、商品をカートに入れるボタンを押すと ActiveRecord::RecordNotFound in CartsController#add_item  Couldn't find Product with 'id'= のエラーが出ました。carts_controllerのadd_itemメソッドの @product =Product.find(params[:product_id]) の箇所のエラーのようです。 ただ、URLがhttp://localhost:3000/products/1/add_itemとなっており、productのidは渡せているようです。なぜこのようなエラーが出るのでしょうか
Cojiro

2020/07/05 13:26

でしたら、params[:id]で取得できると思います。 あまりmemberを使わないので、間違った情報をお伝えしてしまいました。
退会済みユーザー

退会済みユーザー

2020/07/05 14:51

ありがとうございます。今度は ActionController::UrlGenerationError in CartsController#add_item No route matches {:action=>"show", :controller=>"carts", :id=>"1"} missing required keys: [:product_id] が出ました。 「カートに追加」をクリックした時に、ユーザーidと紐づいた、cartを生成しなければいけないのですがそれができていないことによりこのエラーが出るのかな、と思いますがどうでしょうか?
winterboum

2020/07/05 22:52

kojiro12345さんお気になさらずに。 kyuboさん。エラーを一部だけ切り出してるのでわかりにくいです。 で、 routes.rbを改めて眺めてみるとこのネストおかしいですね。 cartはuserに繋がるものですから、productの下にネストするのは??? 仮にuserの下にネストしてあったにしても必要あります?userが復数cartを持つわけではないでしょうから。 慣れないうちはネストのないフラットなroutesの方が混乱しませんよ と思うのですが、kojiro12345さんどう思われます?
Cojiro

2020/07/06 00:45

wnterboumさんありがとうございます。 同感です。 私もルーティングに違和感を感じていました。。。 直感的にもREST的にもproductにadd_itemっていうのは違和感がありました。 おっしゃる通り、慣れないうちはフラットなroutesの方がいいと思います。 ネストだったりネストしたがる発想は無駄に複雑にしていくことが多いですしね。。。 そしてシンプルにRESTでのルーティング設計を試みるのがいいのかなと思います。 また、カートに関してもおっしゃるようにネストさせる必要ないと思います。 非ログインのカートも取り扱うのであれば、ネストさせてはいけないと思います。まぁ今回だとおそらくcartにuser_id必須っぽいので考慮不要そうですが。
guest

0

ベストアンサー

def current_cart @cart =Cart.find(session[:cart_id]) rescue ActiveRecord::RecordNotFound cart = Cart.create session[:cart_id] = cart.id cart end

の cart をみな @cart にしてください。
多分それで行けるかと

ただちょっと気になるのは、ここで 例外 を使っていること。
きちんと条件分けが出来る場合は使わないほうが良い、となんかで読んだ気がします

def current_cart @cart = Cart.find_by(id: session[:cart_id]) || Cart.create session[:cart_id] = @cart.id @cart end

投稿2020/07/05 04:08

編集2020/07/05 04:13
winterboum

総合スコア23284

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

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

退会済みユーザー

退会済みユーザー

2020/07/05 05:00

ご回答いただきありがとうございます。 ご指摘いただいた箇所を修正いたしましたが、エラー内容に変更はありませんでした。 productのidとcartのidが送信できていないことに原因がある気がするのですがいかがでしょうか?
winterboum

2020/07/05 05:16

ごめん、そうです。current_caart定義で来てるかな、ってみてそちらに問題があったのでそこで思考停止してました。 =f.hidden_field :product_id, value: @product.id が問題ありそう。 @product って @product = CartItem.new ですね。まずこれ止めましょう。やるなら @cart_item = CartItem.newですがこれもうあるので。 @product = Product.new ってしたかった? で、これですとsaveしていないので @product.id は nilです。 ここに入れるべきは - @products.each do |product| の productです =f.hidden_field :product_id, value: product.id
退会済みユーザー

退会済みユーザー

2020/07/05 07:10

ご回答いただきありがとうございます。 ご指摘いただいた該当箇所を、=f.hidden_field :product_id, value: product.idに変更いたしましたが、エラー内容は変わりません。 直前の =form_for([@product, @cart_item], url: add_item_products_path, method: :post) do |f| という記述には不自然な点はないでしょうか?テーブルとのリレーションなどと併せてかなり不安要素に思っています。
winterboum

2020/07/05 07:32

私ですとシンプルに form_for [product,current_cart] do で済ませるんじゃないかな。 それでできた htmlの <form>のところのactionを確認してもらえますか
退会済みユーザー

退会済みユーザー

2020/07/05 07:50

form_for([@product, @cart_item], url: add_item_products_path, method: :post) do |f| form_for [product,current_cart] do いずれの場合においても action=/products/add_item になっています。
winterboum

2020/07/05 07:57 編集

rails routes 実行結果見せてください。 インデント崩れるとわかりにくいので、質問欄に<code>で
退会済みユーザー

退会済みユーザー

2020/07/05 08:09 編集

かしこまりました。 質問に追記いたします
winterboum

2020/07/05 10:58

add_item_product_path(product)  ですね
退会済みユーザー

退会済みユーザー

2020/07/05 12:39

何度もご回答ありがとうございます。 修正後、カートに追加ボタンを押すと ActiveRecord::RecordNotFound in CartsController#add_item  Couldn't find Product with 'id'= のエラーが出ます。場所はcarts_controllerのadd_itemの @product =Product.find(params[:product_id])の箇所のようです。 ただ、URLがhttp://localhost:3000/products/1/add_itemとなっており、productのidは渡せているようです。なぜこのようなエラーが出るのでしょうか 貴重なお時間を私のために最低値抱いてありがとうございます。 私の浅い理解のせいで全然解決しなくて本当に申し訳ないです。
winterboum

2020/07/05 13:07

正しく 1 がわたってますね Product.find(params[:product_id]) でなく Product.find(params[:id]) です。 パラメータの確認をするばあいは logをみると良いです
退会済みユーザー

退会済みユーザー

2020/07/05 14:52

ありがとうございます。今度は ActionController::UrlGenerationError in CartsController#add_item No route matches {:action=>"show", :controller=>"carts", :id=>"1"} missing required keys: [:product_id] が出ました。 「カートに追加」をクリックした時に、ユーザーidと紐づいた、cartを生成しなければいけないのですがそれができていないことによりこのエラーが出るのかな、と思いますがどうでしょうか?
Cojiro

2020/07/05 15:18

もう少し丁寧に進捗を教えていただけますか? 何をしてどこまでできるようになって、何ができないのかを教えてください。 こちらにはあなたのソースを動作する環境もなく、記載されているソース、文字のみで判断しなければいけなく、かなり大変なのです。 察するに、 redirect_to current_cart でエラーが出ていると思いますが、合っていますか? エラー内容とroutes.rbを見る限り、redirect_to @product, current_cart でどうでしょうか。 下記のページはお読みになられたことはありますか? なければ、大変ためになるので一度お読みください。 https://railsguides.jp/routing.html
Cojiro

2020/07/05 15:35

winterboum さん 同じ返答内容だったので、こちらに返事してしまいました、申し訳ありません。
退会済みユーザー

退会済みユーザー

2020/07/05 16:10

ご回答ありがとうございます。 お伝えする内容が不十分であることで、ご迷惑をおかけして申し訳ございませんでした。 エラー箇所ですが、おっしゃる通りの位置で起きています。 現在解決したいのは、root_pathに設定している、 products#index内の「カートに追加する」をクリックした後、cart_itemテーブルにproductのidとその商品の数を格納したいのですが、エラーが出るという問題です。 ご教授いただいたことを実践したことでproductのidと数は送れていると思います。現在は以下のエラーです。 NoMethodError in CartsController#add_item undefined method `current_cart' for #<Product:0x00007fe0cbf22020> redirect_to @product.current_cart の部分です なお、「カートに入れる」をクリックした後のブラウザのURLは以下の通りです http://localhost:3000/products/1/add_item 質問の本文に記載の、該当のソースコードを最新に更新させていただこうと思います。質問の仕方に問題があることにより、ご迷惑をおかけして申し訳ございませんでした。また、大変参考になるサイトをご紹介いただきありがとうございます。恥ずかしながら読んだことがなかったので、参考にさせていただきます。 もし可能であれば引き続きご回答のほど、よろしくお願いいたします。
Cojiro

2020/07/06 00:54

このまま今の設計で進めるのもいいと思いますが、winterboumさんがいい問題定義をされていますので、焦らずに考えてみるのもいいと思います。 上記に関しては、 product_cart GET /products/:product_id/carts/:id(.:format) carts#show にリダイレクトさせたいと思っていて、 そのためには、 redirect_to [@product, current_cart] かなと思います。 redirect_to @product.current_cartは . ではなく、 ,です。あと[]が必要です。([]は忘れてました。すいません。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問