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

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

ただいまの
回答率

88.64%

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

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 357
退会済みユーザー

退会済みユーザー

前提・実現したいこと

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

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

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

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

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

イメージ説明

該当のソースコード

#routes.rb
root 'products#index'

  resources :products, only: [:index, :new, :create, :edit, :update] do
    resources :carts, only: [:create, :new]
    member do
      post '/add_item' => 'carts#add_item'
      post '/update_item' => 'carts#update_item'
      delete '/delete_item' => 'carts#delete_item'
    end
  end

  resources :users, only: [:show,:edit,:update]

  resources :carts, only: [:show,:create,:new]
#app/views/products/index.html.haml
#トップページと商品の購入ページを兼ねています。

        %ul
      - @products.each do |product|
        =form_for([@product, @cart_item], url: add_item_product_path(product), method: :post) do |f|
          %li.product
            .pro-main
              .pro-left
                .pro-image
                  = image_tag product.image.url, height: "120"
                .pro-name
                  = product.name
              .pro-center
                .pro-explain
                  = product.explain
              .pro-right
                .pro-price
                  = product.price
                  %span
                    円
                =f.hidden_field :product_id, value: product.id
                .pro-qua
                  = f.number_field :quantity, value: "0", style: "text-align:right", min: 0, class: "pro-quantity"
                .pro-cart
                  = f.submit "カートに入れる", class: "cart-in", name: "add_cart"
#application_controller.rb
#current_cartを定義しています
  helper_method :current_cart

  def current_cart
    @cart = Cart.find_by(id: session[:cart_id]) || Cart.create
    session[:cart_id] = @cart.id
    @cart
  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
#products_controller.rb
class ProductsController < ApplicationController

  def index
    @products = Product.all
    @cart_item = CartItem.new
  end

  def new
    @product = Product.new
  end

  def create
    @product = Product.new(product_params)
    if @product.save
      redirect_to root_path
    else
      render @product.current_cart
    end
  end

  private
  def product_params
    params.require(:product).permit(:name, :explain, :image, :price)
  end

end
#cart.rb
class Cart < ApplicationRecord
  has_many :cart_items, dependent: :destroy
  has_many :products, through: :cart_items
  belongs_to :user
end
#cart_item.rb
class CartItem < ApplicationRecord
  belongs_to :product
  belongs_to :cart
end
#product.rb
class Product < ApplicationRecord
  mount_uploader :image, ImageUploader
  has_many :cart_items, dependent: :destroy
  has_many :cart, through: :cart_items
end
#user.rb
class User < ApplicationRecord
  has_one :cart, dependent: :destroy
end
                  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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

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/06 00:35

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

    キャンセル

  • 2020/07/06 01: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

    質問の本文に記載の、該当のソースコードを最新に更新させていただこうと思います。質問の仕方に問題があることにより、ご迷惑をおかけして申し訳ございませんでした。また、大変参考になるサイトをご紹介いただきありがとうございます。恥ずかしながら読んだことがなかったので、参考にさせていただきます。
    もし可能であれば引き続きご回答のほど、よろしくお願いいたします。

    キャンセル

  • 2020/07/06 09:54

    このまま今の設計で進めるのもいいと思いますが、winterboumさんがいい問題定義をされていますので、焦らずに考えてみるのもいいと思います。

    上記に関しては、
    product_cart GET /products/:product_id/carts/:id(.:format) carts#show にリダイレクトさせたいと思っていて、
    そのためには、
    redirect_to [@product, current_cart]
    かなと思います。
    redirect_to @product.current_cartは . ではなく、 ,です。あと[]が必要です。([]は忘れてました。すいません。)

    キャンセル

0

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

routes.rb 該当部分のみ

  member do
    post '/add_item' => 'carts#add_item'
    post '/update_item' => 'carts#update_item'
    delete '/delete_item' => 'carts#delete_item'
  end

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/05 23:51

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

    キャンセル

  • 2020/07/06 07:52

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

    キャンセル

  • 2020/07/06 09:45

    wnterboumさんありがとうございます。

    同感です。
    私もルーティングに違和感を感じていました。。。
    直感的にもREST的にもproductにadd_itemっていうのは違和感がありました。

    おっしゃる通り、慣れないうちはフラットなroutesの方がいいと思います。
    ネストだったりネストしたがる発想は無駄に複雑にしていくことが多いですしね。。。
    そしてシンプルにRESTでのルーティング設計を試みるのがいいのかなと思います。

    また、カートに関してもおっしゃるようにネストさせる必要ないと思います。
    非ログインのカートも取り扱うのであれば、ネストさせてはいけないと思います。まぁ今回だとおそらくcartにuser_id必須っぽいので考慮不要そうですが。

    キャンセル

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

  • ただいまの回答率 88.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る