从购物车中删除产品后无法将其添加回产品模型,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
.
的情况下在一行中编写控制器的描述。
除此之外,您还需要 ProductsController
、CartController
、OrderController
、PaymentsController
等。每个人都应该做一份工作 - 并且做好.
不要在您的控制器中完成所有操作!
当我们向订单中添加订单项时,产品的可用库存当然应该减少。这是一个清晰的业务逻辑示例。
在 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
在电子商务中 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
.
除此之外,您还需要 ProductsController
、CartController
、OrderController
、PaymentsController
等。每个人都应该做一份工作 - 并且做好.
不要在您的控制器中完成所有操作!
当我们向订单中添加订单项时,产品的可用库存当然应该减少。这是一个清晰的业务逻辑示例。
在 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