您的位置:首页 > 编程语言 > Ruby

ruby on rails实例depot(五)——购物车的添加

2011-12-17 17:18 731 查看


(2010-07-13 19:46:10)


转载▼


标签:


ruby


on


rails实例depot


添加购物车

分类: rubyonrails学习
Rails使用基于cookie的做法来实现session的,所谓cookie是指web应用传递给浏览器的一组带命名的的数据。浏览器会将cookie保存在本地计算机上,当浏览器再向web应用发送请求时,会把cookie数据标签也一起带上,后者就可以根据cookie中的信息将这一请求与服务器保存的session信息匹配起来。Rails 为这些底层细节提供了一个简单的抽象接口,让开发者不必操心协议、cookie 之类的事情。在控制器中,Rails 维护了一个特殊的、类似于hash
的集合,名为session。在处理请求的过程中,如果你将一个名/值对保存在这个hash 中,那么在处理同一个浏览器发出的后续请求时都可以获取到该名/值对。

在Depot 应用程序中,我们希望在session 中保存“一个买主的购物车中有什么货品”的信息。Rails可以很容易地把session数据保存在数据库中。我们需要运行几个Rake任务来创建所需要的数据库表。首先,创建一个数据迁移任务来定义session数据表:rake db:sessions:create,然后实施这个迁移任务rake
db:migrate,就把数据表创建出来了。我们需要告诉rails把session数据保存在数据库中,因为默认的是将所有东西都保存在cookie中,做法如下:首先将config目录下的environment.rb文件中的一行:#config.action_controller.session_store = :active_record_store前面的注释符号“#”去掉,激活基于数据库的session存储机制;然后打开app/controlle/application.rb,找到protect_from_forgery #:secret
=> 'aa8daee37f8fc7c6d44e9260bc6b8654'这一行,删掉中间的“#”。

当顾客执行“add to cart”动作时,我们需要做以下这些事情:如果顾客是第一次用到购物车,那么我们要为顾客建立一个购物车对象并将其放入session中,若顾客已经拥有购物车,则需要从session中取出这个对象。根据此功能,我们在store控制器中创建一个find_cart()方法,由于无需将这个方法作为这个控制器中的一个action了,所以声明为private,并要注意之后添加的方法若作为action的话一定要写在private之前。

private

def find_cart

session[:cart] ||= Cart.new

end

以上代码它使用了Ruby 的条件赋值操作符:||=。如果session 的hash 中已经有:cart这个键,上述语句会立即返回:cart 键对应的值;否则,它会首先新建一个cart 对象,将其放入session,并返回新建的对象。

还需要在store的控制器中加入action,名为add_to_cart的方法,这个方法的功能为:从当前的session中取出购物车对象(如果还没有购物车对象,就新建一个),将选中的货品放入购物车,并显示购物车的内容。

def add_to_cart

@cart = find_cart

product = Product.find(params[:id])

@cart.add_product(product)

end

修改views/store/目录下的index.html.erb文件,将id作为一个参数传递给butto_to方法,

<%= button_to "Add to Cart" , :action => 'add_to_cart', :id => product %>

params是Rails的一个重要对象,其中包含了浏览器请求传来的所有参数。按照惯例,params[:id]包含了将被action是偶那个的对象id(主键)。在视图中调用button_to时我们就已经用:id =>product把这个值设置好了。注意这个add_to_cart方法是一个action方法,所以不要再private之后。

再为这个方法添加一个视图文件:add_to_cart.html.erb:

<h2>Your Pragmatic Cart</h2>

<ul>

<% for item in @cart.items %>

<li><%= item.quantity %> × <%=h item.title %></li>

<% end %>

</ul>

其中quantity是商品的数量,我们在购物车中加入货品“数量”的信息,我们建立“购物车货品”模型cart_item,其中的一个对象不仅包括一个product,而且拥有数量的信息。cart_item.erb中的内容如下;

class CartItem

attr_reader :product,:quantity

def initialize(product)

@product = product

@quantity = 1

end

def increment_quantity

@quantity +=1

end

def title

@product.title

end

def price

@product.price * @quantity

end

end

同时修改cart.rb模型中的内容:

class Cart

attr_reader :items

def initialize

@items = []

end

def add_product(product)

current_item = @items.find{|item| item.product == product}

if current_item

current_item.increment_quantity

else

@items << CartItem.new(product)

end

end

end

这时,再次运行程序,打开http://127.0.0.1:3001/store,点击一下"Add to Cart"按键,页面跳转到如下图所示的页面:





退回后,再点击刚才的按键,则跳转到如下图所示的页面:





若顾客买相同的货品,则显示在一行中,并以图中形式呈现。

现在我们已经为买家提供了简单的购物车,但是,这个购物车令我们很担忧,我们可以看到,上图中将货品放入购物车的链接为:http://127.0.0.1:3001/store/add_to_cart/4,最后面的4就是product的id,如果我们手动将4改为其他不存在的id则页面会崩溃:





这个出错页面暴露了太多信息,大多数顾客是看不懂的,所以我们还应该进一步提升系统的安全性和可靠性。我们可以看到错误信息是因为product = Product.find(params[:id])这行代码,如果指定的货品找不到ActiveRecord 会抛出RecordNotFound 异常,我们处理此异常有三个部分:首先,利用Rails 的日志工具将这一事实记入内部日志文件;其次,向用户输出一条简短的信息(例如“非法货品”之类的);最后,重新显示分类列表页面,以便用户可以继续使用我们的站点。Rails中用flash来处理异常。

当add_to_cart() action 发现传入的货品id 不合法时,它就可以将错误信息保存在flash 中,并重定向到index() action,以便重新显示分类列表页面。index() action 的视图可以将flash 中的错误信息提取出来,在列表页面顶端显示。在视图中,使用flash 方法即可访问到flash 中的信息。flash 数据是保存在session 中的,这样才能够在多个请求之间传递。

修改store.rb中的add_to_cart方法:

def add_to_cart

product = Product.find(params[:id])

@cart = find_cart

@cart.add_product(product)

rescue ActiveRecord::RecordNotFound

logger.error("Attempt to access invalid product #{params[:id]}" )

flash[:notice] = "Invalid product"

redirect_to :action => 'index'

end

rescue 子句拦下了Product.find()抛出的异常,接着我们完成了上述的异常处理的三个部分。运行程序,进行同样的错误测试,打开log目录下的development.log,发现最后几行已经可以看到错误信息了:

Attempt to access invalid product hello

Redirected to actionindex

Completed in 16ms (DB: 16) | 302 Found [http://127.0.0.1/store/add_to_cart/hello]

但是此时的页面只是重定向到了store页面,没有显示出出错的原因,我们应该在视图文件中添加代码,使错误信息显示出来:向store.html.erb添加如下代码:

<% if flash[:notice] -%>

<div id="notice"><%= flash[:notice] %></div>

<% end -%>

将这三行代码放在<%= yield :layout %>这一行前面。这时再次输入错误的id,跳转结果如下所示:





看到了吧,我们处理异常的三个部分都完成了~很容易吧?

还有一个问题,也许你在测试程序的时候已经发现了,那就是我们没有办法在购物车页面删掉又不想购买的商品,这对于顾客是不公平的,下面我们来完成这个功能。我们需要在购物车中加上一个链接,同时在store 控制器中实现empty_cart()方法。

def empty_cart

session[:cart] = nil

flash[:notice] = "Your cart is currently empty"

redirect_to :action => 'index'

end

接着在add_to_cart页面中添加button按钮,在文件的最后添加:<%= button_to 'Empty cart', :action => 'empty_cart' %>





点击“Empty cart”之后,跳转:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: