从购物车中删除产品后无法将其添加回产品模型,Ruby rails

Can't add Products back to Products model after deleting it from Cart, Ruby on rails

在电子商务中 Rails 我正在构建的应用程序从 ShoppingCart 中删除的产品在删除后不会添加回生产模型。

当我将产品添加到购物车时,应用程序使用下面的控制器来减少产品模型中的产品数量(请参阅创建方法)

controllers/product_item_controller.rb

class ProductItemsController < ApplicationController

include CurrentCart

before_action :set_cart, only: [:create]
before_action :set_product_item, only: [:show, :destroy]

def create

    @product = Product.find(params[:product_id])
    @product_item = @cart.add_product(@product.id)
    if @product_item.save
        redirect_to root_url, notice:'Product added to Cart'
        product = Product.find params[:product_id]
        product.update_columns(stock_quantity: product.stock_quantity - 1)
    else
        render :new
    end
end

private

def set_product_item
    @product_item = ProductItem.find(params[:id])
end

def product_item_params
    params.require(:product_item).permit(:product_id)
end

end

这很好。

但是当我删除购物车时,它被删除了,但产品没有添加到产品模型中。我也收到了这条消息:Invalid Cart

这是carts_controller.rb

class CartsController < ApplicationController

before_action :set_cart, only: [:show, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart

def new
    @cart = Cart.new
end

def show
     @images  = ["1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg"]
 @random_no = rand(5)
 @random_image = @images[@random_no]

end


def destroy
    @cart.destroy if @cart.id == session[:cart_id]
    session[:cart_id] = nil
    product = Product.find params[:product_id]
    product.update_columns(stock_quantity: product.stock_quantity + 1)
    redirect_to root_url, notice: 'Your Cart is Empty'
end

def remove
    cart = session['cart']
    item = cart['items'].find { |item| item['product_id'] == params[:id] }

    product = Product.find(item['product_id'])
    product.update_columns(stock_quantity: product.stock_quantity + 1)

    if item
        cart['items'].delete item
    end
  redirect_to cart_path
end


private

def set_cart
    @cart = Cart.find(params[:id])
end

def cart_params
    params[:cart]
end

def invalid_cart
    logger_error = 'You are trying to access invalid cart'
    redirect_to root_url, notice: 'Invalid Cart'
end
end

我看不出这段代码有什么问题,也看不出为什么产品在从购物车中删除后没有添加到 product.rb

我是不是漏掉了什么?有人可以在这里给我建议吗?

以下是其他相关型号和控制器

products_controller.rb

class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]


  def show

  end

def search

@product = Product.search(params[:query]).order("created_at DESC")
@categories = Category.joins(:products).where(:products => {:id => @product.map{|x| x.id }}).distinct

end

private
# Use callbacks to share common setup or constraints between actions.
def set_product
  @product = Product.find(params[:id])
end

# Never trust parameters from the scary internet, only allow the white list through.
def product_params
  params.require(:product).permit(:title, :description, :price_usd, :price_isl, :image, :category_id, :stock_quantity, :label_id, :query)
end
end

Cart.rb型号

class Cart < ActiveRecord::Base

has_many :product_items, dependent: :destroy

def add_product(product_id)
    current_item = product_items.find_by(product_id: product_id)
    if current_item
        current_item.quantity += 1

    else
        current_item = product_items.build(product_id: product_id)
    end
    current_item


end

def total_price_usd
    product_items.to_a.sum{|item| item.total_price_usd}
end

def total_price_isl
    product_items.to_a.sum{|item| item.total_price_isl}
end
end

product.rb型号

Class Product < ActiveRecord::Base
belongs_to :category
belongs_to :label

has_many :product_item, :dependent => :destroy

#before_destroy :ensure_not_product_item



    validates :title, :description, presence: true
    validates :price_usd, :price_isl, numericality: {greater_than_or_equal_to: 0.01}
    validates :title, uniqueness: true


 has_attached_file :image, styles: { medium: "500x500#", thumb: "100x100#" }
 validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/

 #def ensure_not_product_item
 #  if product_item.empty?
 #      return true
 #  else
 #      errors.add(:base, 'You have Product Items')
 #      return false
 #  end

 #end

 def self.search(query)

 where("title LIKE ? OR description LIKE ?", "%#{query}%", "%#{query}%") 
end
end

你有

before_action :set_cart, only: [:show, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart

一旦调用 CartsController#destroy 方法,就会调用私有方法 set_cart。它试图做的是初始化一个实例变量@cart = Cart.find(params[:id])

您的 #destroy 方法的第一行是 @cart.destroy if @cart.id == session[:cart_id]@cart = Cart.find(params[:id]) 不是这里的问题吗? params[:id] 的值是多少?我猜它与 session[:cart_id] 不同,可能是 nil 或数据库无法找到购物车记录的某个整数值,因此出现错误。

编辑 1:

这同样适用于史蒂夫在他的 中提到的 product = Product.find params[:product_id]

Max 发布了一个非常有用的 关于如何正确完成它的信息。如果您有时间坚持他的回答并尝试根据他的建议重新设计您的应用程序。

你正在从 ActiveRecord::RecordNotFound

中营救
rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart

但是您可能不恰当地从 destroy 方法中的 Product.find... 中拯救了...。我不确定为什么您会期望 product_id 在参数中。

您的代码...

def destroy
    @cart.destroy if @cart.id == session[:cart_id]
    session[:cart_id] = nil
    product = Product.find params[:product_id]
    product.update_columns(stock_quantity: product.stock_quantity + 1)
    redirect_to root_url, notice: 'Your Cart is Empty'
end

更好的选择可能是...

def destroy
    if @card.id == session[:cart_id]
      @cart.product_items each do |product_item|
        product_item.product.update_columns(stock_quantity: product_item.product.stock_quantity + 1)
      end
      @cart.destroy
    end
end

然而,这最好作为 product_item 模型的 before_destroy 操作来完成,这样销毁 product_item 将自动增加库存总数。

我不会逐行给出解决方案,因为这个应用程序有很多地方不太正确,需要重新考虑一下。让我们看看常见的购物车是怎么做的。

模特:

class User < ApplicationRecord
  has_many :orders
  has_many :products, through: :orders
  def current_order
    orders.find_or_create_by(status: :open)
  end
end

class Order < ApplicationRecord
  enum status: [:in_cart, :processing, :shipped]
  belongs_to :user
  has_many :line_items
  has_many :products, through: :line_items
end

# The join model between a Order and Product
# The name line item comes from the lines on a order form.
class LineItem < ApplicationRecord
  belongs_to :order
  belongs_to :product
end

class Product < ApplicationRecord
  has_many :line_items
  has_many :orders, through: :line_items
end

这里的命名不是错误的,也不是马虎的复制粘贴。购物车只是 Web 应用程序中的一个概念,它作为创建订单的“用户帮助”存在。

订单和产品之间的连接通常称为订单项。注意我们使用has_many though:这样我们就可以查询:

User.find(1).order
Product.find(1).orders
Order.find(1).products

控制者

当构建像结账这样复杂的东西时,您需要注意单一职责原则和 KISS。拥有很多 类 并不是一件坏事。拥有庞大而混乱的控制器,做的太多了。

因此,例如创建一个控制器,将添加和删除购物车中的项目作为其唯一职责。

# routes.rb
resource :cart
  resources :line_items, 
      only: [:create, :destroy, :update] do

    collection do
      delete :clear
    end
  end
end

# app/controllers/line_items.rb
class LineItemsController < ApplicationController

  before_action :set_cart
  before_action :set_item

  rescue_from Orders::NotOpenError, -> { redirect_to @order, error: 'Order is locked and cannot be edited' }
  
  # Add an item to cart
  # POST /cart/line_items
  def create
    @cart.product_items.create(create_params)
    # ...
  end

  # Remove an item from cart
  # DESTROY /cart/line_items/:id
  def destroy
    @item.destroy
    if @item.destroyed?
      redirect_to cart_path, success: 'Item removed.'
    else
      redirect_to cart_path, alert: 'Could not remove item.'
    end 
  end

  # Remove all items from cart
  # DESTROY /cart/line_items
  def clear
    @order.line_items.destroy_all
    if @order.items.count.zero?
      redirect_to cart_path, success: 'All items destroyed'
    else
      redirect_to cart_path, alert: 'Could not remove all items.'
    end
  end

  # Update a line in the order
  # PATCH /cart/line_items/:id
  def update
    @line_item.update(update_params)
  end

  private
    def set_order
      @order = current_user.current_order
      # Ensure that order is not processed in some way
      raise Orders::NotOpenError unless @order.open?
    end

    def set_line_item
      @line_item = @order.line_items.find(params[:id])
    end

    def create_params
      params.require(:line_item).permit(:product_id, :quantity)
    end

    def update_params
      params.require(:line_item).permit(:quantity)
    end
end

请注意,route each 的路径清楚地告诉我们它的作用,以及我们如何在不使用单词 and.

的情况下在一行中编写控制器的描述。

除此之外,您还需要 ProductsControllerCartControllerOrderControllerPaymentsController 等。每个人都应该做一份工作 - 并且做好.

不要在您的控制器中完成所有操作!

当我们向订单中添加订单项时,产品的可用库存当然应该减少。这是一个清晰的业务逻辑示例。

在 MVC 中,业务逻辑属于模型层。

将商品添加到购物车的用户应该只创建预订。产品的实际库存只应在处理订单或发货时更改:

# No callbacks needed!
class Product < ApplicationRecord
  has_many :line_items
  has_many :orders, through: :line_items
  
  def reservations
    line_items.joins(:order)
              .where
                .not(line_items: {
                  order: Order.statuses[:shipped]
                })
              .sum(:quantity)
  end

  def availibity
    stock - reservations
  end
end