[英]Best technique for “expiring” a shopping cart after a certain amount of time? (rails 3)
我正在用Rails开发一个简单的自定义购物车(是的,重新设计轮子)。 该购物车的功能必须与fab.com类似,因为用户最多只能将其物品存放在其购物车中12分钟。 向购物车中添加任何东西都会重新启动计时器,但是当时间到时,购物车将被清空。
我目前拥有此代码的前端方面的工作(使用简单的jQuery计时器插件)以及购物车上的过期时间戳记,每次添加项目时,时间戳记均已正确设置。 我的问题是:时间到后清空用户购物车的最佳方法是什么? 编辑为了清楚起见:我正在谈论服务器端解决方案,因为此方面的客户端工作正常。 我曾考虑过某种延迟的工作/任务任务,但这似乎过于复杂。 我想正确的解决方案可能是在购物车模型上进行某种回调,该回调在每次更新任何内容时都会检查到期日期?
下面是一些相关的代码片段。
推车型号
class Cart < ActiveRecord::Base
has_many :cart_items
has_many :products, :through => :cart_items
def <<(cart_item)
# add a cart_item to the cart, avoiding redundancy
existing_cart_item = self.cart_items.select{ |ci| ci.product_id == cart_item.product_id }
if existing_cart_item.size > 0
existing_cart_item.first.quantity += cart_item.quantity
existing_cart_item.first.save
else
self.cart_items << cart_item
end
end
def reset_expiry(seconds)
self.expires_at = Time.now + seconds
end
end
购物车物品控制器
class CartItemsController < ApplicationController
def create
@cart_item = CartItem.new(params[:cart_item])
@cart_item.unit_price = @cart_item.product.price
current_cart << @cart_item
current_cart.reset_expiry 720
current_cart.save!
respond_to do |format|
format.html do
flash[:notice] = "Added #{@cart_item.product.name} to cart."
redirect_to current_cart
end
format.json do
render :json => {
:time => 720,
:count => current_cart.total_quantity,
:data => render_to_string(:partial => 'layouts/menu_cart_item.html.erb', :collection => current_cart.cart_items, :as => :item)
}
end
end
end
end
一个可行的解决方案! 看来我在最后一条评论中回答了自己的问题。 感谢@Catcall的想法。
最后,答案非常简单。 我在application_controller
current_cart
方法中添加了一行:
def current_cart
if user_signed_in?
if session[:cart_id]
@current_cart ||= current_user.carts.find(session[:cart_id])
if @current_cart.purchased_at || @current_cart.expired?
session[:cart_id] = nil
end
end
if session[:cart_id].nil?
@current_cart = current_user.carts.create!
session[:cart_id] ||= @current_cart.id
end
end
@current_cart
end
并为cart.rb
模型添加了一些便捷的方法:
def seconds_left
if self.expires_at
return (self.expires_at - Time.now).round
else
-1
end
end
def expired?
if self.expires_at
return (self.expires_at < Time.now)
else
false
end
end
以及product.rb上的一些方法,这样我就可以准确地获取每个项目的最新数量:
def remaining
# starting inventory minus the total quantity of all
# cart items in carts which haven't expired yet or have been purchased
inventory - (cart_items.includes(:cart).where('carts.expires_at > ? OR carts.purchased_at IS NOT NULL',Time.now).to_a.sum(&:quantity))
end
def on_hold
cart_items.includes(:cart).where('carts.expires_at > ? AND carts.purchased_at IS NULL',Time.now).to_a.sum(&:quantity)
end
def purchased
cart_items.includes(:cart).where('carts.purchased_at IS NOT NULL',Time.now).to_a.sum(&:quantity)
end
def on_hold?
(remaining < 1 && on_hold > 0)
end
def sold_out?
(remaining < 1 && on_hold < 1)
end
现在,如果用户当前的购物车已过期,只需为其创建一个新的购物车! 干净整洁。
由于用户可以关闭其浏览器,因此我认为您必须在服务器端执行此操作。 您也许可以从客户端更有效地执行此操作,但是仍然必须考虑封闭的浏览器。 IMO,尽管这会带来一些维护问题,但您可以为两者做一个很好的案例。
每当购物车发生变化时,您都可以尝试更新数据库中的购物车超时值。 然后使用服务器端定时过程,该过程将移动或删除过期购物车中的项目,然后更新(或删除)超时。
定时过程本身可能像单个SQL语句一样简单。 我可能会从类似这样的东西开始,以存储过程的形式编写。
DELETE FROM carts
WHERE cart_expiry_ts < CURRENT_TIMESTAMP;
您必须检查目标平台的文档,才能准确地找到编写有效的存储过程所需的操作。 在PostgreSQL中,我将从这样的东西开始。
CREATE OR REPLACE FUNCTION expire_carts()
RETURNS void AS
'DELETE FROM carts
WHERE cart_expiry_ts < CURRENT_TIMESTAMP;'
LANGUAGE SQL
我将通过执行此语句来运行它。
SELECT expire_carts();
某些dbms平台包括自己的代理,用于在计时器上运行作业。 有些没有。 在没有的平台上,使用系统实用程序。 在Unix变体上通常是cron,在Windows下通常是“计划任务”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.