Ruby on Rails ショッピングカート part6 チェックアウト
Ruby on Rails において ショッピングカート とは以下を示します。
- チェックアウト
ここでは、買い手がカートに入っている商品を実際に買えるように チェックアウト機能を実装する。
- テーブル作成
注文を格納するテーブルを作成する。
- create.sql
create table orders ( id int not null auto_increment, name varchar(100) not null, email varchar(255) not null, address text not null, pay_type char(10) not null, shipped_at datetime null, primary key (id) );
- create.sql
- 既存テーブルを変更
line_itemsテーブルに参照制約を追加する。 これは単にDDLを変更して実行する。 モデルの再作成はしないで、後でbelongs_toを追加するだけ。
- drop.sql
drop table line_items;
- create.sql
create table line_items ( id int not null auto_increment, product_id int not null, order_id int not null, quantity int not null default 0, unit_price decimal(10,2) not null, constraint fk_items_product foreign key (product_id) references products(id), constraint fk_items_order foreign key (order_id) references orders(id), primary key (id) );
- drop.sql
- モデル作成
次のスクリプトでモデルを作成する。 %cd depot/ %ruby script/generate model Order exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/order.rb create test/unit/order_test.rb create test/fixtures/orders.yml exists db/migrate create db/migrate/002_create_orders.rb
- Railsにも変更を伝達
注文には複数の品目が存在することをRailsに明記する。has_many。 品目が注文に属していることもRailsに明記する。belongs_to。
- /app/models/order.rb
class Order < ActiveRecord::Base has_many :line_items end
- /app/models/line_item.rb
class LineItem < ActiveRecord::Base belongs_to :product belongs_to :order def self.for_product(product) item = self.new item.quantity = 1 item.product = product item.unit_price = product.price item end end
- /app/models/order.rb
- メソッドを追加
注文の詳細情報を取得するアクションを定義する。checkout()メソッド。
- /app/controllers/store_controller.rb
def checkout @cart = find_cart @items = @cart.items if @items.empty? redirect_to_index("カートに商品が入っていません!") else @order = Order.new end end
- /app/controllers/store_controller.rb
- ビュー作成
ここでは、checkout.rhtmlを作成する。 中身のコードは暫定的に書きました。
- /app/views/store/checkout.rhtml
<% @page_title = "Checkout" -%> <h3>Please enter your details below</h3> <table> <tr> <td>Name:</td> <td><%= text_field("order", "name", "size" => 40 ) %></td> </tr> <tr> <td>EMail:</td> <td><%= text_field("order", "email", "size" => 40 ) %></td> </tr> <tr valign="top"> <td>Address:</td> <td><%= text_area("order", "address", "cols" => 40, "rows" => 5) %></td> </tr> <tr> <td>Pay using:</td> <td><%= options = [["Select a payment option", ""]] select("order", "pay_type", options) %></td> </tr> <tr> <td></td> <td><%= submit_tag(" CHECKOUT ") %></td> </tr> </table>
- /app/views/store/checkout.rhtml
- チェックアウト実行
とりあえずチェックアウトしてみる!!
- Railsとフォーム
scaffoldジェネレータを使うと、全てのデータを取得するための フォームを自動的に作成してくれた。それは、"_form.rhtml"の おかげで、ビューで(例えばnew.rhtml)そのフォームをサブフォームを 参照している。 <h1>New product</h1> <%= start_form_tag :action => 'create' %> <%= render :partial => 'form' %> <%= submit_tag "Create" %> <%= end_form_tag %> <%= link_to 'Back', :action => 'list' %>
- ビュー編集
先ほどのビューを編集する。 入力された情報(inputタグ)は、モデル内に取得、モデル内に格納、 必要に応じデータベースに格納、というような順番で進められる。
- form_tag()メソッド
htmlのformタグを使用して書くのもありますが、ここでは Railsのヘルパーメソッドform_tag()メソッドを使う。
- /app/views/store/checkout.rhtml
<% @page_title = "チェックアウト" -%> <%= start_form_tag(:action => "save_order") %> <table> <tr> <td>氏名:</td> <td><%= text_field("order", "name", "size" => 40 ) %></td> </tr> <tr> <td>電子メール:</td> <td><%= text_field("order", "email", "size" => 40 ) %></td> </tr> <tr valign="top"> <td>住所:</td> <td><%= text_area("order", "address", "cols" => 40, "rows" => 5) %></td> </tr> <tr> <td>支払い方法:</td> <td><%= options = [["支払い方法を選択してください", ""]] + Order::PAYMENT_TYPES select("order", "pay_type", options) %></td> </tr> <tr> <td></td> <td><%= submit_tag(" チェックアウト ") %></td> </tr> </table> <%= end_form_tag %>
- form_tag()メソッド
- リスト
上記では、選択肢のリストがOrderモデルの属性として格納されていると想定。 この選択肢の配列の定義をorder.rbモデル内に定義。 最初の要素:選択肢として表示される文字列。 2番目の要素:データベースに格納される値。
- /app/models/order.rb
class Order < ActiveRecord::Base has_many :line_items PAYMENT_TYPES = [ [ "小切手", "check" ], [ "クレジットカード", "cc" ], [ "購入注文書", "po" ] ].freeze end
- /app/models/order.rb
- チェックアウト実行
チェックアウトしてみる!! リストの表示もできました!!
- 「チェックアウト」ボタン - メソッド追加
現在、save_order()メソッドを実装していないので、 「チェックアウト」ボタンを押下してもエラー。
- save_order()メソッド
3行目 新しいオブジェクト作成とフォームデータによる初期化 注文オブジェクトに関連つけられているすべてのフォーム データを取得するので、パラメータとして :orderハッシュ を選択している。 4行目 既にカートに格納されている品目を注文に追加する。 5行目 データベースに保存するように指示。また検証も行う。
- /app/controllers/store_controller.rb
def save_order @cart = find_cart @order = Order.new(params[:order]) @order.line_items << @cart.items if @order.save @cart.empty! redirect_to_index('ご注文ありがとうございました') else render(:action => 'checkout') end end
- save_order()メソッド
- 「チェックアウト」実行
実際に注文をしてみる。 しかし、このままでは未入力のままにデータベースに登録できてしまう。
- Rails
「品目テーブルのorder_id列に、参照する注文のidをセットする」 なんて処理はRailsが自動的にしてくれる!!
- Rails
- validate()追加
入力チェックを追加する。また、
- /app/models/order.rb
class Order < ActiveRecord::Base has_many :line_items PAYMENT_TYPES = [ [ "小切手", "check" ], [ "クレジットカード", "cc" ], [ "購入注文書", "po" ] ].freeze validates_presence_of :name, :email, :address, :pay_type end
- /app/models/order.rb
- 「チェックアウト」実行
上記のままでは、save_order()メソッドが実行されても、 "render(:action => 'checkout')"に遷移する。 (URLはsave_orderになっているんだな。) だから、エラー表示を行う必要がある!!
- ビューを編集
以下を2行目に追加する。
- /app/views/store/checkout.rhtml
<%= error_messages_for(:order) %>
- /app/views/store/checkout.rhtml
- 「チェックアウト」実行
ちゃんとエラーが表示されている。リストもエラーに含まれている。
- エラー表示のスタイルを追加
今までは、depot.cssだけを参照していた。 それを、scaffold.cssも参照するように変更。
- /app/views/layouts/store.rhtml
<%= stylesheet_link_tag "scaffold", "depot", :media => "all" %>
- /app/views/layouts/store.rhtml
- 「チェックアウト」実行
ちゃんとエラーにスタイルが付いている
- ビュー編集
チェックアウトページでもカートの内容がほしい。 以下を追加すれば大丈夫。
- /app/views/store/checkout.rhtml
<%= render_component(:action => "display_cart") %>
- /app/views/store/checkout.rhtml
- 「チェックアウト」実行
カートの内容は表示されているが・・・・・。ちょっとおかしい。
- ビュー編集、メソッド修正
display_cart()メソッドに引数を渡して、外側のレイアウトを除いて表示してみる。
- /app/views/store/checkout.rhtml
<%= render_component(:action => "display_cart", :params => { :context => :checkout }) %>
- /app/controllers/store_controller.rb
def display_cart @cart = find_cart @items = @cart.items if @items.empty? redirect_to_index('現在、カートには商品が入っていません') end if params[:context] == :checkout render(:layout => false) end end
- /app/views/store/checkout.rhtml
- 最後に、ビュー編集
display_cart.rhtmlの右側のメニューを表示制御を行う。 あと最後はおまけで<h3>を追加しました。
- /app/views/store/display_cart.rhtml
<div id="cartmenu"> <ul> <li><%= link_to 'ショッピングを続ける', :action => "index" %></li> <% unless params[:context] == :checkout -%> <li><%= link_to 'カートを空にする', :action => "empty_cart" %></li> <li><%= link_to 'チェックアウトする', :action => "checkout" %></li> <% end -%> </ul> </div>
- /app/views/store/checkout.rhtml
<%= render_component(:action => "display_cart", :params => { :context => :checkout }) %> <h3>お客様の連絡先と支払い方法をご指定ください</h3> <%= start_form_tag(:action => "save_order") %>
- /app/views/store/display_cart.rhtml
ご訪問頂き有難う御座います。
当サイトを効率良く使うためにまずは FrontPage を見て下さい。
検索方法、一覧表示などの各情報を纏めています。
当サイトの説明 → Frontpage