👻

【rails】ECサイトの注文機能の実装

2024/11/21に公開

前提

必要なモデル、そのカラムは全て作成が終わっている。
基本的なルーティングも全て終わっている。
ユーザー機能が動いている
商品機能が動いている

この中での作成を始める

コントローラーの作成と基本のアクション

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! は、ログインしている顧客だけがこのコントローラーのアクションを実行できるようにするものです。

ポイント

  1. データの流れを意識:

    • newconfirmcreate の順で注文が作られます。
    • 必要な情報(住所やカート内容など)を少しずつ設定していく仕組みです。
  2. エラー処理が重要:

    • flash[:alert] でエラーメッセージを表示して、問題があるときに正しい画面に戻せるようにしています。
  3. カートのリセット:

    • カート内の商品を全て注文に変換してから削除するので、ユーザーのカートが一貫して管理されます。

各アクションの解説

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
  • ユーザーが入力した注文内容を一時的に確認するためのアクションです。
  1. お届け先の選択 (address_option):

    • case 文で、ユーザーが選んだお届け先のオプションを確認し、それに応じて情報を設定します。
      • "0": 登録済み住所を利用します。
      • "1": ユーザーが保存している配送先リストから選択します。
      • "2": 新しい住所を入力して利用します。
    • 入力に問題があればエラーを出して、前のページに戻します。
  2. カート内商品の情報を取得:

    • @cart_items = current_customer.cart_items で、ログイン中のユーザーのカートに入っている商品をすべて取得します。
  3. 送料と合計金額の計算:

    • 一律送料として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
  • 実際に注文を保存し、データベースに記録します。
  1. 注文の作成と保存:

    • 注文データを作り、カートの内容を利用して注文詳細 (order_details) も作成します。
  2. ステータス管理:

    • @order.is_order = :waiting_for_payment は、注文の初期状態(支払い待ち)を設定しています。
  3. カートの削除:

    • @cart_items.destroy_all で、注文が完了したらカートを空にします。
  4. エラーハンドリング:

    • 保存が失敗した場合、エラーメッセージを表示し、新規作成画面を再表示します。
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