PROGRAMMING

Railsで売上詳細を表示する【ECサイト】

今回はECサイトで売上の詳細を表示する方法を紹介していきたいと思います。

Stripeという決済プラットフォームを使用し、購入履歴を作成します。

そしてその購入履歴を売上として表示するようにします。

作業内容
・Adminアカウントで過去の売上と今月の売上を見れるようにする
・Adminアカウントで売上の詳細を見れるようにする

作業手順

Adminアカウントで売れた商品を取得する

Adminが作成したproductsの中で、売れた商品(purchase_record_products)を検索しなければいけません。そのため、下記のようにjoinsというメソッドを使って、ProductPuchaseRecordProductを結びつけます。

joinsについては以下のリンクで詳しく書いてあります!

https://pikawaka.com/rails/joins

$rails c
2.5.3 :001 >Admin.last.products.joins(:purchase_record_products)
 => #<ActiveRecord::AssociationRelation [#<Product id: 1, admin_id: 1, user_id: nil, name: "test1", description: "これはテスト1用の商品です。", price: 1000, created_at: "2020-10-10 10:14:37", updated_at: "2020-10-10 10:14:37", image: "alvin-mahmudov-vKuEhorbvYI-unsplash__1_.jpg">, #<Product id: 1, admin_id: 1, user_id: nil, name: "test1", description: "これはテスト1用の商品です。", price: 1000, created_at: "2020-10-10 10:14:37", updated_at: "2020-10-10 10:14:37", image: "alvin-mahmudov-vKuEhorbvYI-unsplash__1_.jpg">,

無事に2つのモデルを結ぶことができました。

それではadminモデル下記のようなメソッドを追加します。

class Admin < ApplicationRecord
  devise :database_authenticatable,
         :recoverable, :rememberable, :validatable
  has_many :products, dependent: :destroy
  
  def sales
  end
  
  def sales_one_month
  end 
  
end

salesメソッドにまずは先ほどやったjoinsメソッドを使って購入された商品の一覧を取得します。そして、それを計算して合計値を出します。

  def sales
    sold_products = self.products.joins(:purchase_record_products)
    sold_products.sum(:price)
  end

こんな感じになりますね。めちゃシンプルです。

次にsales_one_monthの方ですね。1ヶ月の売上を見れるようにしなければいけません。

そのためには、購入した今月の売上を検索しなければいけません。検索するには下記のようなコードを追加します。

where(created_at: Time.now.all_month)

これで今月に作成したものを検索することができるそうです。

さらにjoinspurchase_record_productsとテーブルを連結させたので、下記のように結合先のテーブル名を指定して検索をします。

モデル名.joins(:関連名).where(結合先のテーブル名: { カラム名: 値 })

これらを合わせると以下のようになりますね。

  def sales_one_month
    sold_products = products.joins(:purchase_record_products).where({ purchase_record_products: {created_at: Time.now.all_month} })
    sold_products.sum(:price)
  end 

きちんと動作するかどうか確認してみます。

$ rails c 
2.5.3 :001 > Admin.last.sales
  Admin Load (0.3ms)  SELECT  `admins`.* FROM `admins` ORDER BY `admins`.`id` DESC LIMIT 1
   (0.2ms)  SELECT SUM(`products`.`price`) FROM `products` INNER JOIN `purchase_record_products` ON `purchase_record_products`.`product_id` = `products`.`id` WHERE `products`.`admin_id` = 1
 => 14000
2.5.3 :002 > Admin.last.sales_one_month
  Admin Load (0.7ms)  SELECT  `admins`.* FROM `admins` ORDER BY `admins`.`id` DESC LIMIT 1
   (0.3ms)  SELECT SUM(`products`.`price`) FROM `products` INNER JOIN `purchase_record_products` ON `purchase_record_products`.`product_id` = `products`.`id` WHERE `products`.`admin_id` = 1 AND `purchase_record_products`.`created_at` BETWEEN '2020-10-01 00:00:00' AND '2020-10-31 23:59:59'
 => 14000 

無事両方とも動作しました。よかったです。

そしたらこの二つをトップページに表示させます。

売上を表示させる

<div class="container">
  <h2 class="text-center">Adminトップページ</h2>
   <ul class="list-group list-unstyled">
        <li class="admin-sales-all d-flex justify-content-between border">
          <p>今までの売上  <span><%=current_admin.sales.to_s %>円</span></p>
          <a class="p-0 m-0"><%=link_to '詳細', admins_sales_record_path, class: "btn btn-success"%></a>
        </li>
        <li class="admin-sales-one-month d-flex justify-content-between border">
          <p>今月の売上   <span><%=current_admin.sales_one_month.to_s %>円</span></p>
          <a><%=link_to '詳細', admins_sales_record_path, class: "btn btn-success"%></a>
      </li>
    </ul>
   </h2>
</div>

ほとんどカートの中身と表示設定は変わりません。

それでは、売上の詳細を見れるようにします。

売上の詳細を見れるようにする

 resource :sales_record, only: [:show]
class Admins::SalesRecordsController < Admins::ApplicationController
    def show
    end
end

Adminからpurchase_record_productsを閲覧できるようにAdminhas_manyを追加します。

has_many :purchase_record_products, through: :products
<table class="table">
    <thead>
        <tr>
            <th>商品名</th>
            <th>価格</th>
            <th>購入日</th>
        </tr>
    </thead>
    <tbody>
      <% @purchase_record_products.each do |prd| %>
        <% product = prd.product %>
        <tr>
            <td><%=product.name %></td>
            <td><%=product.price %>円</td>
            <td><%=product.created_at %></td>
        </tr>
      <% end %>
    </tbody>
</table>

N+1問題

ここでN+1問題が発生するとのことです。

N+1問題とはなんだ。。

ループ処理の中で都度SQLを発行してしまい、大量のSQLが発行されてパフォーマンスが低下してしまう問題のことです。

日常生活で例えるなら、スーパーで商品を1点ずつお会計するようなもの。
それだけ無駄なことを行なっている状態を指します。

え、そんなアホなこと普通はしないって思いますよね?
でもRailsを使用していると往往にしてこの問題が発生してしまうのです。
引用:https://qiita.com/massaaaaan/items/4eb770f20e636f7a1361

なるほど、めちゃ効率悪いですね。

ということでpreloadメソッドを使って、解決していきます。

@purchase_record_products = current_admin.purchase_record_products.preload(:product)

これだけで問題解決しました。

パラメーターの取得

まずは/adminsindex.html.erbの今月の売上の詳細ボタンを変更していきます。

日付操作に関しましては、下記のページを参考にさせていただきました。

https://qiita.com/mmmm/items/efda48f1ac0267c95c29

<a><%=link_to '詳細', admins_sales_record_path, class: "btn btn-success"%></a>
⬇️
<a><%=link_to '詳細', admins_sales_record_path(start_at: Time.current.beginning_of_month, end_at: Time.current.end_of_month), class: "btn btn-success"%></a>

日付がこれで取得できたはずなので、controllerを変更していきます。

パラメーターから渡される値は文字列なので、日付に変更する必要がありますね。

さて、どうやって日付型にした方がいいのか、ググってみます。

見つけました。https://techacademy.jp/magazine/19845

下のようにすれば日付型に変更できるそうです。

Date.parse("日付")

それでは、start_atend_atを日付型に変更して、検索してみます。

class Admins::SalesRecordsController < Admins::ApplicationController
    
    def show
        @purchase_record_products = current_admin.products.purchase_record_products
        start_at = Date.parse(params[:start_at])
        end_at = Date.parse(params[:end_at])
        @purchase_record_products = @purchase_record_products.where(created_at: start_at..end_at)
    end
end

こんな感じになりますね。しかし、これだと過去全ての売上の場合も実行されてしまい、エラーが起きてしまいますので、if文を追加してあげます。

class Admins::SalesRecordsController < Admins::ApplicationController
    def show
      @purchase_record_products = current_admin.purchase_record_products
      if params[:start_at] && params[:end_at]
          start_at = Date.parse(params[:start_at])
          end_at = Date.parse(params[:end_at])
          @purchase_record_products = @purchase_record_products.where(created_at: start_at..end_at)
      end
    end
end

こんな感じで完成ですね。確認してみましょう。

無事表示されました。

まとめ

これで売上詳細の表示は完成です!

ProductとPurchaseRecordProductを結び付かせるという発想が思いつくまでが難しかったですね。

ECサイトに関する情報が他の記事でも書いているので、もし良ければみてみてください!

最後まで読んで頂きありがとうございます!

-PROGRAMMING

Copyright © Iseblog ,@2020 All Rights Reserved.