当前位置: 代码迷 >> 综合 >> Agile Web Development with Rails第十二章笔记——任务G:付款
  详细解决方案

Agile Web Development with Rails第十二章笔记——任务G:付款

热度:31   发布时间:2023-12-09 08:41:21.0


如标题所示,这一章的主要目的就是完成付款功能,整个任务由以下几个模块迭代完成。

迭代G1:获取订单

订单是商品项目及其购买交易的细节的集合,为了存储购买交易的细节,下面我们需要创建表orders。

使用脚手架创建订单表:
rails generate scaffold order name:string address:text email:string pay_type:string

为了将用户所选商品与订单相关联,为line_item添加属性order_id(是否可以为order添加属性line_item_id)

rails generate migration add_order_id_to_line_item order_id:integer

应用迁移

rake db:migrate

1、创建获取订单的表单

动作设计如下,点击check out按钮,页面跳转至填写订单页面。

                                                              

a、添加check out按钮

[ruby] view plain copy
  1. <!--app\views\carts\_cart.html.erb-->  
  2. <div class="cart_title">Your Cart</div>  
  3. <table>  
  4.   <%= render(cart.line_items) %>  
  5.   <tr class="total_line">  
  6.     <td colspan="2">Total</td>  
  7.     <td class="total_cell"><%= number_to_currency(cart.total_price) %></td>  
  8.   </tr>  
  9. </table>  
  10. <%= button_to "Checkout", new_order_path, :method => :get %>  
  11. <%= button_to 'Empty cart', cart, :method => :delete,  
  12.     :confirm => 'Are you sure?' %>  

b、控制器中判断购物车是否为空(跳至首页给出提示或者跳至新建订单页面)

[ruby] view plain copy
  1. # orders_controller.erb  
  2. def new  
  3.   @cart = current_cart  
  4.   if @cart.line_items.empty?  
  5.     redirect_to store_url, :notice => "Your cart is empty"  
  6.     return  
  7.   end  
  8.   
  9.   @order = Order.new  
  10.   respond_to do |format|  
  11.     format.html # new.html.erb  
  12.     format.xml  { render :xml => @order }  
  13.   end  
  14. end  

完成这一步之后点击测试,新建订单页面如图:

页面标签对应的输入框不够美观,下面通过帮助方法更改输入框的外观类型。

新建订单界面:

[ruby] view plain copy
  1. <!--app\views\orders\new.html.erb-->  
  2. <div class="depot_form">  
  3.   <fieldset>  
  4.     <legend>Please Enter Your Details</legend>  
  5.     <%= render 'form' %>  
  6.   </fieldset>  
  7. </div>  
这个模版利用了一个同文件夹下名叫_form的局部模版
[ruby] view plain copy
  1. <%= form_for(@orderdo |f| %>  
  2.   <% if @order.errors.any? %>  
  3.     <div id="error_explanation">  
  4.       <h2><%= pluralize(@order.errors.count, "error") %>  
  5.       prohibited this order from being saved:</h2>  
  6.   
  7.       <ul>  
  8.       <% @order.errors.full_messages.each do |msg| %>  
  9.         <li><%= msg %></li>  
  10.       <% end %>  
  11.       </ul>  
  12.     </div>  
  13.   <% end %>  
  14.   
  15.   <div class="field">  
  16.     <%= f.label :name %><br />  
  17.     <%= f.text_field :name:size => 40 %>  
  18.   </div>  
  19.   <div class="field">  
  20.     <%= f.label :address %><br />  
  21.     <%= f.text_area :address:rows => 3, :cols => 40 %>  
  22.   </div>  
  23.   <div class="field">  
  24.     <%= f.label :email %><br />  
  25.     <%= f.email_field :email:size => 40 %>  
  26.   </div>  
  27.   <div class="field">  
  28.     <%= f.label :pay_type %><br />  
  29.     <%= f.select :pay_type, Order::PAYMENT_TYPES,  
  30.                   :prompt => 'Select a payment method' %>  
  31.   </div>  
  32.   <div class="actions">  
  33.     <%= f.submit 'Place Order' %>  
  34.   </div>  
  35. <% end %>  
如上所示,通过表单帮助方法定义输入框格式,其中付款方式选项列表中的属性使用模型层中提前定义的数组。
[ruby] view plain copy
  1. class Order < ActiveRecord::Base  
  2.   PAYMENT_TYPES = [ "Check""Credit card""Purchase order" ]  
  3.  end  
接下来就是按书中所说添加相应的CSS

[ruby] view plain copy
  1. /* Styles for order form */  
  2.   
  3. .depot_form fieldset {  
  4.   background: #efe;  
  5. }  
  6.   
  7. .depot_form legend {  
  8.   color: #dfd;  
  9.   background: #141;  
  10.   font-family: sans-serif;  
  11.   padding: 0.2em 1em;  
  12. }  
  13.   
  14. .depot_form label {  
  15.   width: 5em;  
  16.   float: left;  
  17.   text-align: right;  
  18.   padding-top: 0.2em;  
  19.   margin-right: 0.1em;  
  20.   display: block;  
  21. }  
  22.   
  23. .depot_form select, .depot_form textarea, .depot_form input {  
  24.   margin-left: 0.5em;  
  25. }  
  26.   
  27. .depot_form .submit {  
  28.   margin-left: 4em;  
  29. }  
  30.   
  31. .depot_form div {  
  32.   margin: 0.5em 0;  
  33. }  

保护程序,在order模型层对表单传入的值进行判定

[ruby] view plain copy
  1. validates :name:address:email:pay_type:presence => true  
  2. validates :pay_type:inclusion => PAYMENT_TYPES  

页面效果如图:

2、获取订单细节

这部分的主要内容是填写订单页面中信息,将页面信息连同购物车中商品信息一同存入数据库中。

示意图如下:

                                             

首先根据订单与在线商品的关系分别定义其模型层文件

在线商品模型层:

[ruby] view plain copy
  1. class LineItem < ActiveRecord::Base  
  2.   belongs_to :order  
  3.   belongs_to :product  
  4.   belongs_to :cart  
  5.   
  6.   def total_price  
  7.     product.price * quantity  
  8.   end  
  9. end  
订单模型层:(指明订单被销毁时,从属于订单的在线商品也被销毁)
[ruby] view plain copy
  1. class Order < ActiveRecord::Base  
  2.   PAYMENT_TYPES = [ "Check""Credit card""Purchase order" ]  
  3.   validates :name:address:email:pay_type:presence => true  
  4.   validates :pay_type:inclusion => PAYMENT_TYPES  
  5.   has_many :line_items:dependent => :destroy  
  6. end  
最后,订单的create方法被定义如下:

a.用表单数据初始化新建的order对象

b.将购物车中的在线商品添加到order对象中

c.保存order对象

[ruby] view plain copy
  1. def create  
  2.   @order = Order.new(params[:order])  
  3.   @order.add_line_items_from_cart(current_cart)  
  4.   
  5.   respond_to do |format|  
  6.     if @order.save  
  7.       Cart.destroy(session[:cart_id])  
  8.       session[:cart_id] = nil  
  9.       format.html { redirect_to(store_url, :notice =>   
  10.         'Thank you for your order.') }  
  11.       format.xml  { render :xml => @order:status => :created,  
  12.         :location => @order }  
  13.     else  
  14.       format.html { render :action => "new" }  
  15.       format.xml  { render :xml => @order.errors,  
  16.         :status => :unprocessable_entity }  
  17.     end  
  18.   end  
  19. end  
将购物车中的在线商品添加到order对象中的方法定义在order的模型层文件中:

因为下一步要销毁购物车,为免于在线商品被同时销毁,在这一步将在线商品的cart_id设为nil。

同时将在线商品本身添加到订单的line_items集合中。

[ruby] view plain copy
  1. def add_line_items_from_cart(cart)  
  2.   cart.line_items.each do |item|  
  3.     item.cart_id = nil  
  4.     line_items << item  
  5.   end  
  6. end  

到此为止,获取表单信息的任务完成,下面我们进行相关测试

必填项测试:


订单创建成功:


迭代G2:分页

分页是一个Web应用必不可少的功能,Rails中的分页可以借助于will_paginate插件实现。

1、修改Gemfile文件说明使用插件的意图(指明Rails的版本需要大于或等于3.0 pre)

[ruby] view plain copy
  1. gem 'will_paginate''>= 3.0.pre'  
修改完成之后使用bundle install命令来安装依赖

2、在script目录下创建文件用来生成测试数据

生成100个测试数据

[ruby] view plain copy
  1. Order.transaction do  
  2.   (1..100).each do |i|  
  3.     Order.create(:name => "Customer #{i}":address => "#{i} Main Street",  
  4.       :email => "customer-#{i}@example.com":pay_type => "Check")  
  5.   end  
  6. end  
使用命令运行测试脚本:rails runner script/load_orders.rb
3、修改控制器以调用paginate插件

[ruby] view plain copy
  1. def index  
  2.   @orders = Order.paginate :page=>params[:page], :order=>'created_at desc',  
  3.     :per_page => 10  
  4.   
  5.   respond_to do |format|  
  6.     format.html # index.html.erb  
  7.     format.xml  { render :xml => @orders }  
  8.   end  
  9. end  

4、在视图中增加链接

[ruby] view plain copy
  1. <h1>Listing orders</h1>  
  2.   
  3. <table>  
  4.   <tr>  
  5.     <th>Name</th>  
  6.     <th>Address</th>  
  7.     <th>Email</th>  
  8.     <th>Pay type</th>  
  9.     <th></th>  
  10.     <th></th>  
  11.     <th></th>  
  12.   </tr>  
  13.   
  14. <% @orders.each do |order| %>  
  15.   <tr>  
  16.     <td><%= order.name %></td>  
  17.     <td><%= order.address %></td>  
  18.     <td><%= order.email %></td>  
  19.     <td><%= order.pay_type %></td>  
  20.     <td><%= link_to 'Show', order %></td>  
  21.     <td><%= link_to 'Edit', edit_order_path(order) %></td>  
  22.     <td><%= link_to 'Destroy', order, :confirm => 'Are you sure?',  
  23.               :method => :delete %></td>  
  24.   </tr>  
  25. <% end %>  
  26. </table>  
  27.   
  28. <br />  
  29.   
  30. <%= link_to 'New Order', new_order_path %>  
  31. <p><%= will_paginate @orders %></p>  
展示效果如下:



  相关解决方案