Rails|ECサイトの注文画面・確認画面・注文確定機能
要件
ECサイトの注文機能を作成しています。
カートに入れた商品(CartItem)の一覧表示、「注文情報入力進む」をクリック
→ 注文情報入力画面(支払い方法、お届け先を選択)、「確認画面へ進む」をクリック
→ カートに入れた商品の一覧表示、支払い方法、お届け先を表示、「注文を確定する」をクリック
→ 注文完了画面
マイページの「注文履歴一覧」をクリック
→ 注文履歴の一覧を表示、「詳細表示」をクリック
→ 注文履歴の詳細を表示
という流れにします。
開発環境
ruby 3.1.2p20
Rails 6.1.7.4
Cloud9
devise (4.9.2)
Memberモデル、Itemモデル、CartItemモデル、Addressモデルを作成済み
enum導入済み、ja.ymlファイル作成済み
モデルの作成
orderモデルを作成します。
$ rails g model order
ordersテーブルの中身はこちらの記事の通りです。
定義書の通りに、migrationファイルを編集します。
create_table :orders do |t|
t.integer "member_id", null: false
t.string "post_code", null: false
t.text "address", null: false
t.string "name", null: false
t.integer "shipping_fee", null: false
t.integer "total_price", null: false
t.integer "pay_method", null: false
t.integer "status", null: false
t.timestamps
end
db:migrateを実行します。
$ rails db:migrate
コントローラの作成
次にコントローラを作成します。
$ rails g controller orders new show index complete confirm
rails g controller コントローラ名 アクション名
このようにコマンドを実行することで、アクションとビューを同時に作成できます。
class OrdersController < ApplicationController
before_action :authenticate_member!, only: [:new, :confirm, :create, :indexm :show, :complete]
def new
end
def confirm
@cart_items = CartItem.where(member_id: current_member.id)
@shipping_fee = 800 #送料は800円で固定
@selected_pay_method = params[:order][:pey_method]
#以下、商品合計額の計算
ary = []
@cart_items.each do |cart_item|
ary << cart_item.item.price*cart_item.quantity
end
@cart_items_price = ary.sum
@total_price = @shipping_fee + @cart_items_price
@address_type = params[:order][:address_type]
case @address_type
when "member_address"
@selected_address = current_member.post_code + " " + current_member.address + " " + current_member.family_name + current_member.first_name
when "registered_address"
unless params[:order][:registered_address_id] == ""
selected = Address.find(params[:order][:registered_address_id]
@selected_address = selected.post_code + " " + selected.address + " " + selected.name
else
render :new
end
when "new_address"
unless params[:order][:new_post_code] == "" && params[:order][:new_address] == "" && params[:order][:new_name] == ""
@selected_address = params[:order][:new_post_code] + " " + params[:order][:new_address] + " " + params[:order][:new_name]
else
render :new
end
end
end
def create
@order = Order.new
@order.member_id = current_member.id
@order.shipping_fee = 800
@cart_items = CartItem.where(member_id: current_member.id)
ary = []
@cart_items.each do |cart_item|
ary << cart_item.item.price*cart_item.quantity
end
@cart_items_price = ary.sum
@order.total_price = @order.shipping_fee + @cart_items_price
@order.pay_method = params[:order][:pay_method]
if @order.pay_method == "credit_card"
@order.status = 1
else
@order.status = 0
end
address_type = params[:order][:address_type]
case address_type
when "member_address"
@order.post_code = current_member.post_code
@order.address = current_member.address
@order.name = current_member.family_name + current_member.first_name
when "registered_address"
Addresse.find(params[:order][:registered_address_id])
selected = Addresse.find(params[:order][:registered_address_id])
@order.post_code = selected.post_code
@order.address = selected.address
@order.name = selected.name
when "new_address"
@order.post_code = params[:order][:new_post_code]
@order.address = params[:order][:new_address]
@order.name = params[:order][:new_name]
end
if @order.save
if @order.status == 0
@cart_items.each do |cart_item|
OrderDetail.create!(order_id: @order.id, item_id: cart_item.item.id, price: cart_item.item.price, quantity: cart_item.quantity, making_status: 0)
end
else
@cart_items.each do |cart_item|
OrderDetail.create!(order_id: @order.id, item_id: cart_item.item.id, price: cart_item.item.price, quantity: cart_item.quantity, making_status: 1)
end
end
@cart_items.destroy_all
redirect_to complete_orders_path
else
render :items
end
end
end
def index
@orders = Order.where(member_id: current_member.id).order(created_at: :desc).
end
def show
@order = Order.find(params[:id])
@order_details= OrderDetail.where(order_id: @order.id)
end
def complete
end
end
ビューの作成
注文情報入力画面
ここで支払い方法とお届け先を選択します。
ここで入力した情報は orders#confirm
に送られます。
<div class="container p-3">
<div class="row">
<%= render 'public/shared/header.html.erb' %>
<div class="col-md-12">
<h3>注文情報入力</h3>
<%= form_with model: Order.new, url: confirm_orders_path, method: :get do |f| %>
<h4>支払い方法</h4>
<%= f.radio_button :pay_method, :credit_card, checked: true %>
<%= f.label :pay_method, Order.pay_methods_i18n[:credit_card], {value: :credit_card} %>
<br>
<%= f.radio_button :pay_method, :transfer %>
<%= f.label :pay_method, Order.pay_methods_i18n[:transfer], {value: :transfer} %>
<div>
<h4>お届け先</h4>
<%= f.radio_button :address_type, "member_address", checked: true %>
<%= f.label :address_type, "ご自身の住所" %>
〒<%= current_member.post_code + " " + current_member.address + " " + current_member.family_name + current_member.first_name %>
</div>
<br>
<%= f.radio_button :address_type, [:registered_address] %>
<%= f.label :address_type, "登録済住所から選択" %>
<%= f.select :registered_address_id, current_member.addresses.all.map { |m| [[m.post_code, m.address, m.name].join(" "), m.id] } ,include_blank: "登録してある配送先住所から選択" %>
<br>
<%= f.radio_button :address_type, "new_address" %>
<%= f.label :address_type, "新しいお届け先" %>
<table>
<tr>
<td>郵便番号(ハイフンなし)</td>
<td><%= f.text_field :new_post_code %></td>
</tr>
<tr>
<td>住所</td>
<td><%= f.text_field :new_address %></td>
</tr>
<tr>
<td>宛名</td>
<td><%= f.text_field :new_name %></td>
</tr>
</table>
<br>
<%= f.submit "確認画面へ進む", class: "btn btn-info" %>
<% end %>
</div>
<%= render 'public/shared/footer.html.erb' %>
</div>
</div>
f.radio_button
ラジオボタンについての解説はこちら
Order.pay_methods_i18n[:credit_card]
enumを利用しています。credit_cardは0、transferは1に紐付けていて、ja.ymlに日本語訳を作成しています。そのため、この部分は「クレジットカード」と表示されます。
配送先住所について
各ラジオボタンに address_type を作成しています。
自分の住所 → member_address
自分の登録済み住所 → registered_address
新しい住所 → new_address
この値を使って、コントローラで条件分岐をさせています。
<%= f.select :registered_address_id, current_member.addresses.all.map { |m| [[m.post_code, m.address, m.name].join(" "), m.id] } ,include_blank: "登録してある配送先住所から選択" %>
プルダウンで登録済みの住所から選択できるようになっています。
:registered_address_id
このフォームから送付するデータは params[:order][:registered_address_id]
で送られます。
current_member.addresses.all
現在ログイン中のユーザーに紐づけられた全Addressデータを取得します。
map
メソッドを使って、各Addressのデータ(郵便番号、住所、宛名)を取り出して join
メソッドで繋げています。プルダウンの選択肢の表示用です。
m.id
各アドレスのidもパラメータとして送っています。コントローラでデータを扱う用です。
注文確認画面
注文情報入力画面の内容を確認させます。
<div class="container p-3">
<div class="row">
<%= render 'public/shared/header.html.erb' %>
<div class="col-md-12">
<h3>注文情報確認</h3>
<table class="table">
<tr>
<td>商品名</td>
<td>単価(税込)</td>
<td>数量</td>
<td>小計</td>
</tr>
<% @cart_items.each do |cart_item| %>
<tr>
<td>
<%= image_tag cart_item.item.image, size: "100x80" %>
<strong><%= cart_item.item.name %></strong>
</td>
<td><%= cart_item.item.price %></td>
<td><%= cart_item.quantity %></td>
<td><%= cart_item.item.price*cart_item.quantity %></td>
</tr>
<% end %>
</table>
<table class="table">
<tr>
<td>送料</td>
<td><%= @shipping_fee %></td>
</tr>
<tr>
<td>商品合計</td>
<td><%= @cart_items_price %></td>
</tr>
<tr>
<td>請求額</td>
<td><%= @total_price %></td>
</tr>
</table>
<h4>支払い方法</h4>
<% if @selected_pay_method == "credit_card" %>
クレジットカード
<% else %>
銀行振込
<% end %>
<h4>お届け先</h4>
<%= @selected_address %>
<%= form_with url: orders_path, method: :post do %>
<%= hidden_field_tag 'order[pay_method]', @selected_pay_method %>
<%= hidden_field_tag 'order[address_type]', @address_type %>
<% if @address_type == "registered_address" %>
<%= hidden_field_tag 'order[registered_address_id]', params[:order][:registered_address_id] %>
<% elsif @address_type == "new_address" %>
<%= hidden_field_tag 'order[new_post_code]', params[:order][:new_post_code] %>
<%= hidden_field_tag 'order[new_address]', params[:order][:new_address] %>
<%= hidden_field_tag 'order[new_name]', params[:order][:new_name] %>
<% end %>
<%= submit_tag "注文を確定する", class: "btn btn-success" %>
<% end %>
</div>
<%= render 'public/shared/footer.html.erb' %>
</div>
</div>
f.hidden_field
隠しフィールドを使って、データを送付します。
注文完了画面
<div class="container p-3">
<div class="row">
<%= render 'public/shared/header.html.erb' %>
<div class="col-md-12">
<h3>ご注文ありがとうございました!</h3>
</div>
<%= render 'public/shared/footer.html.erb' %>
</div>
</div>
注文履歴一覧画面
<div class="container p-3">
<div class="row">
<%= render 'public/shared/header.html.erb' %>
<div class="col-md-12">
<h3>注文履歴一覧</h3>
<table class="table">
<tr>
<td>注文日</td>
<td>配送先</td>
<td>注文商品</td>
<td>支払い金額</td>
<td>ステータス</td>
<td>注文詳細</td>
</tr>
<% @orders.each do |order| %>
<tr>
<td><%= order.created_at.strftime('%Y/%m/%d') %></td>
<td><%= order.address %></td>
<td>
<ul class="list-unstyled">
<% order.order_details.each do |order_detail| %>
<li><%= order_detail.item.name %></li>
<% end %>
</ul>
</td>
<td><%= order.total_price %></td>
<td><%= order.status_i18n %></td>
<td><%= link_to "表示する", order_path(order.id), class: "btn btn-info" %></td>
</tr>
<% end %>
</table>
<%= paginate @orders, theme: 'bootstrap-5' %>
</div>
<%= render 'public/shared/footer.html.erb' %>
</div>
</div>
注文履歴詳細画面
<div class="container p-3">
<div class="row">
<%= render 'public/shared/header.html.erb' %>
<div class="col-md-12">
<h3>注文履歴詳細</h3>
<h4>注文情報</h4>
<table class="table">
<tr>
<td>注文日</td>
<td><%= @order.created_at.strftime('%Y/%m/%d') %></td>
</tr>
<tr>
<td>配送先</td>
<td>〒<%= @order.post_code + " " + @order.address + " " + @order.name %></td>
</tr>
<tr>
<td>支払い方法</td>
<td><%= @order.pay_method_i18n %></td>
</tr>
<tr>
<td>ステータス</td>
<td><%= @order.status_i18n %></td>
</tr>
</table>
<h4>請求情報</h4>
<table class="table">
<tr>
<td>商品合計</td>
<td><%= @order.total_price %></td>
</tr>
<tr>
<td>配送料</td>
<td><%= @order.shipping_fee %></td>
</tr>
<tr>
<td>ご請求額</td>
<td><%= @order.total_price + @order.shipping_fee %></td>
</tr>
</table>
<h4>注文内容</h4>
<table class="table">
<tr>
<td>商品</td>
<td>単価(税込)</td>
<td>個数</td>
<td>小計</td>
</tr>
<% @order_details.each do |order_detail| %>
<tr>
<td><%= order_detail.item.name %></td>
<td><%= order_detail.item.price %></td>
<td><%= order_detail.quantity %></td>
<td><%= order_detail.quantity * order_detail.item.price %></td>
</tr>
<% end %>
</table>
</div>
<%= render 'public/shared/footer.html.erb' %>
</div>
</div>
参考にさせていただいた記事等
Discussion