iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 9
1
Software Development

PHP新手30天實戰金流系列 第 9

[Day9] 金流平台的主要函式之概觀

PHP新手30天實戰金流, Laravel6

前言

參考此平台架構概觀, 測試範例試跑

主要 Model

一個購物平台主要的三個 Model:

  1. 用戶
  2. 商品
  3. 訂單

Model 相關的 Table

商品

產生兩張 table (products, product_skus) 來管理。

  1. Product :

    Schema::create('products', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->text('description');
            $table->string('image');
            $table->boolean('on_sale')->default(true);
            $table->float('rating')->default(5);
            $table->unsignedInteger('sold_count')->default(0);
            $table->unsignedInteger('review_count')->default(0);
            $table->decimal('price', 10, 2);
            $table->timestamps();
        });
    
    • 取出庫存:
    public function skus()
    {
        return $this->hasMany(ProductSku::class);
    }
    
  2. ProductSku : Stock Keeping Unit,簡稱SKU,定義為保存庫存控制的最小可用單位

    Schema::create('product_skus', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('description');
            $table->decimal('price', 10, 2);
            $table->unsignedInteger('stock');
            $table->unsignedInteger('product_id');
            $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
            $table->timestamps();
        });
    
    • foreign_key 另外定義; onDelete('cascade') 意思為刪掉 product 其27種規格的庫存也會被刪掉
    • 範例資料:
      • 此範例之商品各有三種屬性,因此有27種組合,分別記錄個組合的庫存量。也就是說 product_skus 中的 id 1~27 的 product_id 欄位都是 '1'
  3. ProductSkuAttribute : 商品屬性-規格大小顏色

    Schema::create('product_sku_attributes', function (Blueprint $table) {
                $table->increments('id');
                $table->string('name');
                $table->text('items');
                $table->unsignedInteger('product_id');
                $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
                $table->timestamps();
            });
    
    • 範例資料:

訂單

產生兩張 table (orders, order_iterms) 來管理。

  1. Order : 沒有任何商品資訊
    Schema::create('orders', function (Blueprint $table) {
            $table->increments('id');
            $table->string('no')->unique();
            $table->unsignedInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->text('address');
            $table->decimal('total_amount', 10, 2);
            $table->text('remark')->nullable();
            $table->dateTime('paid_at')->nullable();
            $table->string('payment_method')->nullable();
            $table->string('payment_no')->nullable();
            $table->string('refund_status')->default(\App\Models\Order::REFUND_STATUS_PENDING);
            $table->string('refund_no')->unique()->nullable();
            $table->boolean('closed')->default(false);
            $table->boolean('reviewed')->default(false);
            $table->string('ship_status')->default(\App\Models\Order::SHIP_STATUS_PENDING);
            $table->text('ship_data')->nullable();
            $table->text('extra')->nullable();
            $table->timestamps();
        });
    
  2. OrderItem:
    Schema::create('order_items', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('order_id');
            $table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade');
            $table->unsignedInteger('product_id');
            $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
            $table->unsignedInteger('product_sku_id');
            $table->foreign('product_sku_id')->references('id')->on('product_skus')->onDelete('cascade');
            $table->unsignedInteger('amount');
            $table->decimal('price', 10, 2);
            $table->unsignedInteger('rating')->nullable();
            $table->text('review')->nullable();
            $table->timestamp('reviewed_at')->nullable();
        });
    

關聯性 table:

  • cart_items:
Schema::create('cart_items', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->unsignedInteger('product_sku_id');
            $table->foreign('product_sku_id')->references('id')->on('product_skus')->onDelete('cascade');
            $table->unsignedInteger('amount');
        });

Model 中的購物相關函式

  • User

    1. cartItems
  • Product

    1. attrs
    2. skus
    • views/products/show.blade.php 會用到
      @foreach($product->attrs as $attr_index => $attr)
  • Product_sku

    1. product
    2. decreaseStock
    3. addStock

    -- 好像沒有用到的函式 getAttrsAttribute --

    public function getAttrsAttribute()
    {
        $attrs = $this->product->attrs;
        $attr_items_index = json_decode($this->attr_items_index, true);
        return collect($attr_items_index)->mapWithKeys(function ($item_index, $index) use ($attrs) {
            $attr = $attrs->get($index);
            return [$attr->name => $attr->items[$item_index]];
        });
    }
    
    • 看不太懂
    function ($item_index, $index) use ($attrs) {
            $attr = $attrs->get($index);
            return [$attr->name => $attr->items[$item_index]];
        }
    
  • Order

    1. boot
    2. user
    3. items
    4. findAvailableNo
    5. getAvailableRefundNo
      --
    • user 函式是做什麼用的?
      OrderController 的 store 會用到
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    
    • boot
    protected static function boot()
    {
        parent::boot();
        // 監聽模型建立事件,在寫入 DB之前觸發
        static::creating(function ($model) {
            // 如果模型的 no 字段为空
            if (!$model->no) {
                // 調用 findAvailableNo 生成訂單流水號
                $model->no = static::findAvailableNo();
                // 如果失敗,則终止建立訂單
                if (!$model->no) {
                    return false;
                }
            }
        });
    }
    
  • OrderItem

    1. product
    2. productSku
    3. order
  • CartItem

    1. user
    2. productSku

Attribute Casting

將資料在 DB 儲存的型態轉換成一般較通用的型態,方便操作。

  1. int -> boolean
  2. text/Json -> array
  3. timestamp -> datetime

Controller

  • Cart
  • Orders
  • Payment
  • Products

Controller 裡的 function

  • Cart

    • add
    • remove
  • Orders

    • store
    • index
    • show
    • receive
    • review
    • sendReview
    • applyRefund
      --
    • Store
    public function store(OrderRequest $request, OrderService $orderService)
        {
            $user = $request->user();
            $address = UserAddress::find($request->input('address_id'));
            $currency_code="THB";
    
            return $orderService->store($user, $address, $request->input('remark'), $request->input('items'),$currency_code);
        }
    
    • address_id 是什麼?
    • 為什麼不在 function完成 store就好, 還要寫一個 OrderService?

    猜...處理資料庫事務程式碼太多, 不宜放在高層

  • Payment

    • payByXXXXX
    • XXXXXNotify
    • afterPaid
    • XXXXXRefundNotify
  • Products

    • index

    • show

    • favor

    • disfavor

    • favorites

    • show product 會去撈出該商品的27種規格的庫存:

      • 程式碼:
      $skus = $product->skus->map(function ($sku) {
      return [
          'id' => $sku->id,
          'price' => $sku->price,
          'stock' => $sku->stock,
          'attr_items_index' => json_decode($sku->attr_items_index, true),
      ];
      });
      dd($skus);
      

綠界測試範例

  • 將範例存檔為 "test.php",與 sdk 放在同一目錄下並執行php test.php

會回傳測試用的付款畫面


上一篇
[Day8] 金流是什麼
下一篇
[Day10] ECPay API 參數了解
系列文
PHP新手30天實戰金流34
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言