Ruby on ralis 购物车功能实现(一)

来源:互联网 发布:java计算器界面代码 编辑:程序博客网 时间:2024/06/16 09:36

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

在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'这一行,删掉中间的“#”。



分类显示

ruby script/generate controller store index

 

class StoreController < ApplicationController
  def index

    @products = Product.find_products_for_sale
  end
end

 

class Product < ActiveRecord::Base
  def self.find_products_for_sale
    find(:all, :order => "title" )
  end

 

depot_d/app/views/store/index.html.erb
<h1>Your Pragmatic Catalog</h1>
<% for product in @products -%>
<div class="entry">
<%= image_tag(product.image_url) %>
<h3><%=h product.title %></h3>
<%= product.description %>
<div class="price-line">
<span class="price"><%= product.price %></span>
</div>
</div>
<% end %>

h(string)方法对货品名称中出现的HTML 元素进行转义处理,但并没有对货品
描述做同样的处理。这样一来,我们就可以在其中加入HTML 样式

 

  添加页面布局

在Rails 中,定义和使用布局的方式有好多种,我们现在就使用最简单的一种。如果我们在app/views/layouts目录中创建了一个与某个控制器同名的模板文件,那么该控制器所渲染的视图在默认状态下会使用此布局模板。所以,我们现在就来创建这样一个模板。我们的控制器名叫store ,所以这个布局模板的名字就应该是store.html.erb 。

<!DOCTYPE html PUBLIC "//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.dtd" >
<html>
 <head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag "depot" , :media => "all" %>
 </head>
 <body id="store">
  <div id="banner">
   <%= image_tag("logo.png" ) %>
   <%= @page_title || "Pragmatic Bookshelf" %>
  </div>
  <div id="columns">
   <div id="side">
    <a href="http://www....">Home</a><br />
    <a href="http://www..../faq">Questions</a><br />
    <a href="http://www..../news">News</a><br />
    <a href="http://www..../contact">Contact</a><br />
   </div>
   <div id="main">
    <%= yield :layout %>
   </div>
  </div>
 </body>
</html> 

 

 

 当我们调用yield 方法并传入:layout时, Rails 会自动在这里插入页面的内容

 

用辅助方法格式化价格 

<span class="price" ><%= product.price %></span>
修改为
<span class="price"><%= number_to_currency(product.price) %></span>

 

链接到购物车

link_to()辅助方法生成的是<a href=...> 这样一个HTML 标签;当你点击链接时,浏览器会向服务器发起一个HTTP GET 请求。而HTTP GET 请求不应该用来改变服务器上任何东西的状态——只应该用于获取信息。

 

:method=>:post 可以解决这个问题。此外button_to()方法也可以从视图链回应用程序,但它生成的是一个HTML 表单,其中只包含一个按钮。当用户点击这个按钮时,浏览器会向服务器发起一个HTTP POST 请求。

 

<div class="price-line">
  <span class="price"><%= number_to_currency(product.price) %></span>
  <%= button_to "Add to Cart" %>
</div>


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

    private
    def find_cart
       session[:cart] ||= Cart.new
    end

或者

private
  def find_cart

  unless session[:cart] # i除非存在了,否则新建一个
      session[:cart] = Cart.new # add a new one
    end
    session[:cart] # return existing or new cart

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之后

   

创建购物车 

 

创建cart.rb文件,把它放在app/models 目录 

class Cart
 attr_reader :items
 def initialize
  @items = []
 end
 def add_product(product)
  @items << product
 end
end 

 


另一个聪明一点的购物车


给购物车中的货品加上“数量”信息。于是我们决定新建一个模型类CartItem ,其中引用一种货品,并包含数量信息。 每次初始化一个CartItem,数量为一。

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 类的add_product()方法中使用这个新建的模型类了 

 def add_product(product)
  current_item = @items.find {|item| item.product == product}

#判断是否存在,存在当前的item,则购物车数量增加1,否则把CartItem放进items里头
  if current_item
   current_item.increment_quantity
  else
   @items << CartItem.new(product)
  end
 end

 

对add_to_cart视图做了简单的修改,将“数量”这项新的信息显示出来。 

<li><%= item.quantity %> &times; <%=h item.title %></li>

 

add_to_cart这个方法添加一个视图文件: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>


点击Add to Cart按钮

NoMethodError in StoreController#add_to_cart

undefined method `product' for #<Product:0x3880b00>

session 中的购物车对象还是旧版本的:它直接把货品放进@items 数组。所以,当Rails 从session 中取出这个购物车之后,里面装的都是Product 对象,而不是CartItem 对象。

最简单的办法就是删除旧的session,把原来那个购物车留下的所有印记都抹掉。由于我们使用数据库来保存session 数据,只要一个Rake 命令就可以轻松地清空sessions 表。

rake db:sessions:clear

 

假设Depot 应用已经发布了一个版本,其中使用了旧的Cart 对象。现在有成千上万的顾客正在忙碌地购物,而我们又决定要发布新的、更好用的购物车。我们把代码投入正式运行,突然间所有正在享受购物乐趣的顾客们在往购物车中添加货品时就会遇到错误。而我们唯一的解决办法是删除session数据,也就是说所有顾客的购物车都会被清空。
这个例子告诉我们,“把应用层面的对象放在session中”通常不是个好主意:如果这样做的话,一旦我们把修改之后的应用程序投入实际运行,就必须清空所有现存的session数据。

所以,推荐的做法是只在session 中保存尽可能简单的东西:字符串、数字,等等。应用层面的对象应该放在数据库里,然后将它们的主键放入session,需要时根据session中的主键来查找对象



0 0
原创粉丝点击