iT邦幫忙

2022 iThome 鐵人賽

DAY 18
0
Modern Web

跳脫MVC,Laravel + React 建立電商網站系列 第 18

Day 18 Laravel + React 實戰之路 -3(基本資料表建置)

  • 分享至 

  • xImage
  •  

昨天有說到今天的目標就是設計資料表,前面的兩天都是在做環境基礎的建置跟理解,今天終於開始要真正踏入「類電商」這個專案了。
基本上在進行一個專案的時候就是要先想一下他的架構我們要弄多大,基本上我先做最基本的定義,後續如果要長大就在這個基礎上長大吧!

我們要做一個電商,最基本的就是要有一個商品的資料表,商品的資料表至少要有:商品名稱、商品敘述(如果架構大一些 可能會有 分類、原產地、品牌、供應商....之類的各種資訊)
這時候就會有人疑問了,那基本的還有:商品的價格跟商品圖片呢?
因為我覺得價格可能不只一個(可能會有不同會員不同價格的事情)
相同地,一個上架的商品也有可能有多張圖,在這種情況下,我就不會把它設計成欄位,我會把這兩個東西都設計成兩張表,來方便可以做一對多的關聯

有了商品,使用者也是很重要的一環,除非我們想做一頁式電商XD
因為Users的表在最一開始Laravel 就幫我們建好了,這裡也想重新設計一下Users的表。
我們規劃一下關於使用者的資料表:
首先,我想要把使用者分級,用來劃分出不同使用者看到的不同價格跟畫面,所以我們需要一個type來區分。

接著,我們想一次最基本的電商邏輯運作,我們會發現 應該還需要一張表來記錄訂單資訊:
訂單基本上就包含很多內容了,我先把我想到的寫出來:訂單編號(流水號)、user_id(哪個使用者的)、訂單狀態(我想設計細一些的狀態,包含 準備、已出貨....)
、連絡電話、運送地址(我們假設都只有宅配XD,懶得再去搞超商了 ps.反正我沒有業主)、金額(錢最重要!)

有了訂單之後還需要有一張表來記錄:"這筆訂單購買了哪些商品",基本上這張表可以想像成一張中間表,用來存放 訂單id跟商品id的比對

這裡我補充一下:為了讓這個專案不要無限擴大化,我們必須在某些地方做出妥協,例如:只能宅配、只能貨到付款(謎之音:你是不是不會串金流Api)
這裡的限制都是為了讓我能夠有機會做出成品,用心良苦阿!

我在腦裡跑了一次的購買流程,感覺差不多就這樣了,什麼購物車之類的到時候再說,反正也不一定都要記在資料庫裡,比如:記在瀏覽器就是一個很棒的事情 XD

【小提醒】
關於資料庫正規化,網路上有很多文件,還有三少原則之類的,我覺得理念 + 經驗 就會變成實務上很棒的資料庫設計模式,同樣的需求會有不一樣面向的設計方式,因人而異囉!
ps.因為資料表設計也是水很深的一門技術,所以我這裡的專案會以"不考慮"資料表擴充性的方式下去做最簡化的設計


DB Schema
粗略畫一下,基本的關聯跟架構 大概就是醬
https://ithelp.ithome.com.tw/upload/images/20220922/20145703yhKwbeguje.png


Migration 實作:
我們第一步要先把"主表"實作出來,然後跑一次Migration,主表分別是:User、Products、Orders
我們在原本的user migration中加入:

$table->enum('type',['NORMAL','SUPER','STAFF'])->default('NORMAL')->index()->comment('會員等級(普通、超級、員工)');

這裡我補充一下,我們要創建migration的時候,直接使用指令:

php artisan make:migration create_{table_name}_table

用指令的好處是 Laravel會自己幫我們帶入 檔名的時間。

接下來的兩個主表我就直接上code了
products

Schema::create('products', function (Blueprint $table) { 
    $table->id(); 
    $table->string('name')->index()->comment('商品名稱'); 
    $table->text('description')->comment('商品敘述'); 
    $table->timestamps(); 
});

Orders

Schema::create('orders', function (Blueprint $table) { 
    $table->id(); 
    $table->foreignUuid('number')->comment('訂單編號'); 
    $table->foreignIdFor(User::class); 
    $table->enum('type',['PREPARE','DELIVERY','COMPLETE','RETURN'])->default('PREPARE')->index()->comment('狀態(準備中、配送中、完成、退貨)'); 
    $table->string('phone_number',15)->comment('手機號碼'); 
    $table->string('address')->index()->comment('運送地址'); 
    $table->bigInteger('price')->unsigned()->comment('訂單金額'); 
    $table->timestamps(); 
});

關於phone_number 我想講一下,千萬不要使用int,因為開頭的0會被省略,然後就會出事
為什麼我說 這裡先只做主表呢?
因為migration有個很酷的功能是可以使用 foreignIdFor 這個函式,使用 foreignIdFor 的結果就是會創造一個欄位,屬性跟指定的model 的id一樣,然後也會幫你做好欄位名稱
是不是很讚讚阿!不過這裡要放的是Model,所以我們等等要把主表的model建出來 最後再寫其他的關聯表Migration

結果:
https://ithelp.ithome.com.tw/upload/images/20220922/20145703s5mC9rqdHT.png

https://ithelp.ithome.com.tw/upload/images/20220922/20145703UcXQs2A9CQ.png

Migrations 這張表是自動生成的,裡面會記錄透過migration創建出來的資料表,如果檔案已經紀錄在這張表之中,那下次執行php artisan migrate 的時候就會跳過。

好的,現在主表已經建立完成,先來針對主表建例Model,關聯的部分先不用,有用到再寫,就寫個最基本的model就好
首先,我先建立一個BaseModel,讓之後其他建立的Model可以直接extend就有基本的設定
BaseModel的一開始設定沒有很多,先這樣就好

<?php 
namespace App\Models; 
use Illuminate\Database\Eloquent\Model; 
class BaseModel extends Model 
{ 
    protected $connection = 'mysql'; 
}

接下來,我們讓其他Model去extends這個BaseModel

接下來就是建立基本的Model

Product

<?php 
namespace App\Models; 
class Product extends BaseModel 
{ 
    protected $table = 'products'; 
    protected $guarded = []; 
}

Order

<?php 
namespace App\Models; 
class Order extends BaseModel 
{ 
    protected $table = 'orders'; 
    protected $guarded = []; 
}

這樣兩個基本的Model就可以使用了

備註:這裡我要建立其他Model去繼承的時候踩了個坑,我很確信我的命名有遵從psr-4的規範,然後composer.json也確實有

"App\\": "app/",

這句話,但我就是沒辦法繼承BaseModel,然後我有觀察到 phpStorm有些怪怪的,有很多檔案都找不到Q
最後的解法是上網找的:先把.idea這個檔案殺掉(這個是php storm用來記錄配置的檔案),然後再到 FIle < Invalidate Caches 重開就好了 ...

我們再回去建置剩下的Migration
接下來先一次把所有檔案建起來~語法跟上面的一樣 我就直接上圖

https://ithelp.ithome.com.tw/upload/images/20220922/20145703D5qKmN56zE.png

好的,接下來我直接上code,因為只是照著上面畫好的寫而已,就不特別解釋了
oreder_details

Schema::create('order_details', function (Blueprint $table) { 
    $table->id(); 
    $table->foreignIdFor(\App\Models\Order::class); 
    $table->foreignIdFor(\App\Models\Product::class); 
    $table->bigInteger('price')->unsigned()->comment('當下購買商品的金額'); 
    $table->timestamps(); 
});

product_prices

Schema::create('product_prices', function (Blueprint $table) { 
    $table->id(); 
    $table->foreignIdFor(\App\Models\Product::class)->comment('商品id'); 
    $table->enum('user_type',['NORMAL','SUPER','STAFF'])->default('NORMAL')->index()->comment('會員等級(普通、超級、員工)'); 
    $table->bigInteger('price')->unsigned()->comment('商品單價'); 
    $table->timestamps(); 
});

product_image

Schema::create('product_images', function (Blueprint $table) { 
    $table->id(); 
    $table->foreignIdFor(\App\Models\Product::class)->comment('商品id'); 
    $table->text('image_path')->comment('圖片網址'); 
    $table->timestamps(); 
});

最後最後 再下一次 php artisan migrate

https://ithelp.ithome.com.tw/upload/images/20220922/20145703UF7MhH3Qsq.png

終於結束了,剩下的我們明天見~


上一篇
Day 17 Laravel + React 實戰之路 -2(登入套件理解)
下一篇
Day 19 Laravel + React 實戰之路 -4(假資料)
系列文
跳脫MVC,Laravel + React 建立電商網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言