👻
【rails】ECサイトの注文機能の実装
前提
必要なモデル、そのカラムは全て作成が終わっている。
基本的なルーティングも全て終わっている。
ユーザー機能が動いている
商品機能が動いている
この中での作成を始める
コントローラーの作成と基本のアクション
public/orders_controller.rb
class Public::OrdersController < ApplicationController
before_action :authenticate_customer!
def new
@order = Order.new
end
def confirm
# 注文情報を生成
@order = current_customer.orders.build(order_params)
# お届け先を選択
case params[:order][:address_option]
when "0"
# 登録済み住所を使用
@order.address = current_customer.address
@order.post_code = current_customer.post_code
@order.name = "#{current_customer.last_name} #{current_customer.first_name}"
when "1"
# 選択した配送先住所を使用
address = current_customer.addresses.find_by(id: params[:address_id])
if address.nil?
flash[:alert] = "選択した配送先が見つかりません。"
redirect_to new_public_order_path
return
end
@order.address = address.address
@order.post_code = address.post_code
@order.name = address.name
when "2"
# 新しい住所を使用
@order.address = params[:order][:address]
@order.post_code = params[:order][:post_code]
@order.name = params[:order][:name]
else
# 無効な選択肢
flash[:alert] = "正しいお届け先を選択してください。"
redirect_to new_public_order_path
return
end
# カートアイテムを取得
@cart_items = current_customer.cart_items
# 合計金額と送料を計算
@order.postage = 800 # 一律送料
@order.total_amount = @cart_items.sum do |cart_item|
cart_item.item.price * cart_item.amount
end
end
def create
@order = current_customer.orders.build(order_params)
@cart_items = current_customer.cart_items
@order.is_order = :waiting_for_payment # ここでステータスを設定 (例えば、最初は「支払い待ち」)
@order.postage = 800
@order.total_amount = @cart_items.sum { |cart_item| cart_item.item.price * cart_item.amount }
if @order.save
@cart_items.each do |cart_item|
@order.order_details.create!(
item_id: cart_item.item_id,
price: cart_item.item.price,
quantity: cart_item.amount,
is_production: 'not_started' # 'your_value_here'を適切な値に変更
)
end
@cart_items.destroy_all
redirect_to url_for(controller: 'public/orders', action: 'thanks')
else
# エラー内容を出力
puts "注文保存エラー: #{@order.errors.full_messages}"
flash[:alert] = "注文の保存に失敗しました。内容を確認してください。"
render :new
end
end
def thanks
end
def index
@orders = current_customer.orders
end
def show
@order = Order.find(params[:id])
@order_details = @order.order_details
end
private
# order_paramsメソッドを定義して、必要なパラメータを許可
def order_params
params.require(:order).permit(:address, :post_code, :name, :payment_method, :total_amount, :postage, :is_order, :address_id, :address_option)
end
end
クラスの概要
-
Public::OrdersController
は、注文機能を制御するコントローラー。 -
before_action :authenticate_customer!
は、ログインしている顧客だけがこのコントローラーのアクションを実行できるようにするものです。
ポイント
-
データの流れを意識:
-
new
→confirm
→create
の順で注文が作られます。 - 必要な情報(住所やカート内容など)を少しずつ設定していく仕組みです。
-
-
エラー処理が重要:
-
flash[:alert]
でエラーメッセージを表示して、問題があるときに正しい画面に戻せるようにしています。
-
-
カートのリセット:
- カート内の商品を全て注文に変換してから削除するので、ユーザーのカートが一貫して管理されます。
各アクションの解説
confirm 注文確認画面に表示させたいって事
public/orders_controller.rb
def confirm
# 注文情報を生成
@order = current_customer.orders.build(order_params) # ログイン中の顧客の注文情報をフォームの入力データで作成
# お届け先を選択
case params[:order][:address_option] # フォームから送られた address_option に基づいて処理を分岐
when "0"
# 登録済み住所を使用
@order.address = current_customer.address # 顧客の登録住所を注文のお届け先住所に設定
@order.post_code = current_customer.post_code # 顧客の郵便番号を設定
@order.name = "#{current_customer.last_name} #{current_customer.first_name}" # 顧客の氏名を設定
when "1"
# 選択した配送先住所を使用
address = current_customer.addresses.find_by(id: params[:address_id]) # フォームで選択された配送先のIDで住所を取得
if address.nil? # 配送先が見つからない場合
flash[:alert] = "選択した配送先が見つかりません。" # エラーメッセージを表示
redirect_to new_public_order_path # 新規注文ページにリダイレクト
return # これ以降の処理を中断
end
@order.address = address.address # 選択した配送先の住所を注文情報に設定
@order.post_code = address.post_code # 選択した配送先の郵便番号を設定
@order.name = address.name # 選択した配送先の名前を設定
when "2"
# 新しい住所を使用
@order.address = params[:order][:address] # フォームで入力された新しい住所を設定
@order.post_code = params[:order][:post_code] # フォームで入力された郵便番号を設定
@order.name = params[:order][:name] # フォームで入力された名前を設定
else
# 無効な選択肢
flash[:alert] = "正しいお届け先を選択してください。" # エラーメッセージを表示
redirect_to new_public_order_path # 新規注文ページにリダイレクト
return # これ以降の処理を中断
end
# カートアイテムを取得
@cart_items = current_customer.cart_items # 顧客のカートに入っている商品情報を取得
# 合計金額と送料を計算
@order.postage = 800 # 一律送料を設定
@order.total_amount = @cart_items.sum do |cart_item| # 合計金額を計算
cart_item.item.price * cart_item.amount # 各商品の価格 × 個数 を合計
end
end
- ユーザーが入力した注文内容を一時的に確認するためのアクションです。
-
お届け先の選択 (
address_option
):-
case
文で、ユーザーが選んだお届け先のオプションを確認し、それに応じて情報を設定します。-
"0"
: 登録済み住所を利用します。 -
"1"
: ユーザーが保存している配送先リストから選択します。 -
"2"
: 新しい住所を入力して利用します。
-
- 入力に問題があればエラーを出して、前のページに戻します。
-
-
カート内商品の情報を取得:
-
@cart_items = current_customer.cart_items
で、ログイン中のユーザーのカートに入っている商品をすべて取得します。
-
-
送料と合計金額の計算:
- 一律送料として800円を設定。
- カート内商品の価格と数量から合計金額を計算します。
create 注文確認画面で作成するって事
public/orders_controller.rb
def create
@order = current_customer.orders.build(order_params) # フォームの入力情報で新しい注文を作成
@cart_items = current_customer.cart_items # 顧客のカートアイテムを取得
@order.is_order = :waiting_for_payment # 注文の初期ステータスを「支払い待ち」に設定
@order.postage = 800 # 一律送料を設定
@order.total_amount = @cart_items.sum { |cart_item| cart_item.item.price * cart_item.amount } # 合計金額を計算
if @order.save # 注文情報を保存
@cart_items.each do |cart_item| # カート内の商品ごとに注文詳細を作成
@order.order_details.create!(
item_id: cart_item.item_id, # 商品IDを設定
price: cart_item.item.price, # 商品の単価を設定
quantity: cart_item.amount, # 商品の注文数を設定
is_production: 'not_started' # 生産ステータスを「未開始」に設定
)
end
@cart_items.destroy_all # 注文が完了したらカートを空にする
redirect_to url_for(controller: 'public/orders', action: 'thanks') # 注文完了ページにリダイレクト
else
# エラー内容を出力
puts "注文保存エラー: #{@order.errors.full_messages}" # コンソールにエラー内容を表示
flash[:alert] = "注文の保存に失敗しました。内容を確認してください。" # エラーメッセージを表示
render :new # 新規注文ページを再表示
end
end
- 実際に注文を保存し、データベースに記録します。
-
注文の作成と保存:
- 注文データを作り、カートの内容を利用して注文詳細 (
order_details
) も作成します。
- 注文データを作り、カートの内容を利用して注文詳細 (
-
ステータス管理:
-
@order.is_order = :waiting_for_payment
は、注文の初期状態(支払い待ち)を設定しています。
-
-
カートの削除:
-
@cart_items.destroy_all
で、注文が完了したらカートを空にします。
-
-
エラーハンドリング:
- 保存が失敗した場合、エラーメッセージを表示し、新規作成画面を再表示します。
index 注文一覧(注文履歴)を表示させたいって事
public/orders_controller.rb
def index
@orders = current_customer.orders # ログイン中の顧客のすべての注文を取得
end
- ログイン中の顧客が行ったすべての注文を一覧表示します。
show 注文詳細を表示させたいって事
public/orders_controller.rb
def show
@order = Order.find(params[:id]) # URLのIDパラメータで指定された注文を取得
@order_details = @order.order_details # 注文に紐づく詳細情報を取得
end
- 特定の注文の詳細を表示します。
- 注文の詳細データ (
@order_details
) を取得して、それをビューで表示します。
ビューの作成
注文情報入力画面
order/new.html.erb
<h1>注文情報入力</h1>
<%= form_with model: @order, url: confirm_public_orders_path, method: :post do |f| %>
<div>
<h3>お届け先</h3>
<!-- 登録済み住所 -->
<label><%= f.radio_button :address_option, "0" %> 登録済み住所:<%= current_customer.address %></label><br>
<!-- 配送先住所選択 -->
<label><%= f.radio_button :address_option, "1" %> 配送先住所選択:
<%= select_tag :address_id, options_from_collection_for_select(current_customer.addresses, :id, :address), prompt: "配送先住所を選択" %>
</label><br>
<!-- 新規住所 -->
<label><%= f.radio_button :address_option, "2" %> 新規住所</label><br>
<%= text_field_tag 'order[address]', nil, placeholder: "住所" %><br>
<%= text_field_tag 'order[post_code]', nil, placeholder: "郵便番号" %><br>
<%= text_field_tag 'order[name]', nil, placeholder: "名前" %><br>
</div>
<div>
<h3>支払い方法</h3>
<label><%= f.radio_button :payment_method, "credit_card" %> クレジットカード</label><br>
<label><%= f.radio_button :payment_method, "bank_transfer" %> 銀行振込</label>
</div>
<%= f.submit "確認画面へ進む" %>
<% end %>
注文確認画面
order/confirm.html.erb
<h3>注文内容確認</h3>
<p>お届け先: <%= @order.post_code %> <%= @order.address %> <%= @order.name %></p>
<p>支払い方法: <%= @order.payment_method == "credit_card" ? "クレジットカード" : "銀行振込" %></p>
<h4>注文商品一覧</h4>
<% @cart_items.each do |cart_item| %>
<p><%= cart_item.item.name %>: <%= cart_item.amount %>個</p>
<% end %>
<%= form_with model: @order, url: public_orders_path, method: :post do |f| %>
<%= f.hidden_field :address, value: @order.address %>
<%= f.hidden_field :post_code, value: @order.post_code %>
<%= f.hidden_field :name, value: @order.name %>
<%= f.hidden_field :payment_method, value: @order.payment_method %>
<!-- 注文内容の合計金額などの表示 -->
<p>合計金額: <%= number_to_currency(@order.total_amount, unit: "円") %></p>
<p>送料: <%= number_to_currency(@order.postage, unit: "円") %></p>
<p>注文金額: <%= number_to_currency(@order.total_amount + @order.postage, unit: "円") %></p>
<%= f.submit "注文を確定する" %>
<% end %>
注文完了画面
order/thanks.html.erb
<h3>ご注文ありがとうございました!</h3>
<p>またのご利用をお待ちしております。</p>
<%= link_to "トップページへ戻る", top_path %>
注文一覧(注文履歴)
order/index.html.erb
<h1>注文履歴一覧</h1>
<table>
<thead>
<tr>
<th>注文日</th>
<th>注文番号</th>
<th>合計金額</th>
<th>注文状況</th>
<th></th>
</tr>
</thead>
<tbody>
<% @orders.each do |order| %>
<tr>
<td><%= order.created_at.strftime("%Y年%m月%d日") %></td>
<td><%= order.id %></td>
<td><%= number_to_currency(order.total_amount + order.postage, unit: "¥") %></td>
<td><%= order.is_order %></td>
<td><%= link_to "詳細を見る", public_order_path(order), class: "btn btn-primary" %></td>
</tr>
<% end %>
</tbody>
</table>
注文詳細
order/show.html.erb
<h1>注文詳細</h1>
<h3>注文情報</h3>
<p>注文番号: <%= @order.id %></p>
<p>注文日: <%= @order.created_at.strftime("%Y年%m月%d日") %></p>
<p>注文状況: <%= @order.is_order %>
<p>支払い方法: <%= @order.payment_method_i18n %></p>
<h3>お届け先情報</h3>
<p>宛名: <%= @order.name %></p>
<p>住所: <%= @order.post_code %> <%= @order.address %></p>
<h3>注文商品</h3>
<table>
<thead>
<tr>
<th>商品名</th>
<th>単価</th>
<th>数量</th>
<th>小計</th>
</tr>
</thead>
<tbody>
<% @order.order_details.each do |order_detail| %>
<tr>
<td><%= order_detail.item.name %></td>
<td><%= number_to_currency(order_detail.price, unit: "¥") %></td>
<td><%= order_detail.quantity %></td>
<td><%= number_to_currency(order_detail.price * order_detail.quantity, unit: "¥") %></td>
</tr>
<% end %>
</tbody>
</table>
<h3>合計金額</h3>
<p>商品合計: <%= number_to_currency(@order.total_amount, unit: "¥") %></p>
<p>送料: <%= number_to_currency(@order.postage, unit: "¥") %></p>
<p>合計金額: <%= number_to_currency(@order.total_amount + @order.postage, unit: "¥") %></p>
Discussion