现在的位置: 首页 > 综合 > 正文

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

2017年11月14日 ⁄ 综合 ⁄ 共 4511字 ⁄ 字号 评论关闭
 (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 %> &times; <%=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"按键,页面跳转到如下图所示的页面:

ruby <wbr>on <wbr>rails实例depot(五)鈥斺敼何锍档奶砑

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

ruby <wbr>on <wbr>rails实例depot(五)鈥斺敼何锍档奶砑
若顾客买相同的货品,则显示在一行中,并以图中形式呈现。

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

ruby <wbr>on <wbr>rails实例depot(五)鈥斺敼何锍档奶砑
    这个出错页面暴露了太多信息,大多数顾客是看不懂的,所以我们还应该进一步提升系统的安全性和可靠性。我们可以看到错误信息是因为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,跳转结果如下所示:

ruby <wbr>on <wbr>rails实例depot(五)鈥斺敼何锍档奶砑

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

    还有一个问题,也许你在测试程序的时候已经发现了,那就是我们没有办法在购物车页面删掉又不想购买的商品,这对于顾客是不公平的,下面我们来完成这个功能。我们需要在购物车中加上一个链接,同时在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' %>

ruby <wbr>on <wbr>rails实例depot(五)鈥斺敼何锍档奶砑
点击“Empty cart”之后,跳转:

抱歉!评论已关闭.