iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Modern Web

Laravel Filament 從入門到實戰系列 第 11 篇

Day 10:表格裡顯示關聯資料,一眼看出這張訂單屬於哪個客戶

  • 分享至 

  • xImage
  •  

Day 09:表單裡選關聯資料,下拉選單背後在做什麼 結尾留下一句話,訂單建立完成後回到訂單列表頁看一眼,會發現一個新的落差冒出來,表格上如果顯示客戶欄位,看到的只會是一串客戶主鍵的數字,7、12、3,使用者完全看不出這張訂單屬於哪個客戶。表單端已經處理好選客戶時看到的是店名,表格端還完全沒有處理。

今天要處理的正是這件事。

訂單列表上的那串數字,沒有人看得懂

打開訂單列表頁,訂單編號、訂單狀態、訂單日期這幾個欄位都還算清楚,唯獨客戶欄位顯示的是 customer_id 這個外鍵本身的值。畫面上一整欄排下來,7、12、3、7、9,這些數字對電腦來說是精確無誤的索引,對盯著螢幕的使用者而言,卻等於什麼都沒說。

這個落差造成的困擾很具體。業務人員想確認某張訂單是哪家寵物用品店下的,看著一串數字完全無從判斷,需要一個一個點開訂單編輯頁,才能查出對應的客戶名稱。

訂單筆數還是個位數的時候,這樣做頂多多花幾秒鐘,勉強能忍受。

但 Day 01:為什麼是 Filament,跟手刻後台與其他套件比起來差在哪 已經定案這間批發商的業務語意,訂單量不算巨大但頻率高,一旦訂單持續累積,這個查找成本會迅速變得不合理,光是想確認今天早上這五張訂單分別是哪些客戶下的,就得點開五次編輯頁面,來回五趟重複的流程。

今天的任務範圍很明確,讓訂單列表頁的客戶欄位直接顯示客戶名稱,使用者掃過列表就能看出每一張訂單屬於誰,不需要額外點開才知道。

同時,還要補上依客戶篩選訂單這類延伸情境,方便業務人員只看某一家客戶的所有訂單紀錄。

今天不處理一張訂單裡實際包含哪些商品這種巢狀資料,訂單依然當成一筆獨立資料處理,這件事留到後面才展開。

表格欄位跨表取值,跟表單端是同一份關聯定義

在動手調整之前,先把背後的機制想清楚。訂單列表頁要顯示的客戶名稱,資料實際存放在客戶資料表裡,不在訂單資料表本身,訂單資料表裡存的只是 customer_id 這個外鍵主鍵。這正是 Day 09:表單裡選關聯資料,下拉選單背後在做什麼 已經拆解過的關聯欄位儲存機制,畫面上看到的文字跟資料庫裡儲存的值是兩件事。今天要處理的,是這份關聯反過來在表格端如何被讀出並顯示。

如果借用一個比喻,表格上顯示的名字,其實是隔壁那張表借過來的。訂單資料表自己身上沒有店名這個欄位,它只留了一個指向客戶資料表的路標,customer_id。

表格欄位要顯示店名,得先照著這個路標走到客戶資料表,把對應那一列的 name 欄位借出來,貼在訂單列表這一格上。

在 Laravel Filament 裡面,實際寫法很直觀易懂,TextColumn 本身就支援用小數點 . 串接關聯名稱與欄位名稱,一次到位:

use Filament\Tables\Columns\TextColumn;

TextColumn::make('customer.name')
    ->label('所屬客戶')
    ->searchable()
    ->sortable(),

customer.name 這串寫法,前半段 customer 對應的正是 Day 9 在 Order Model 裡定義好的 belongsTo 關聯方法,後半段 name 則是客戶資料表上實際存放店名的欄位。這跟 Day 9 表單端 relationship('customer', 'name') 指定的是同一份關聯、同一個顯示欄位,只是換了一種語法呈現,表單端用方法參數指定,表格端用小數點串接欄位路徑指定。學會其中一邊,另一邊不需要重新學一套邏輯,兩者共用的都是 Order.php 裡那個 customer() 方法背後代表的 Eloquent 關聯。

這裡要簡短提一個新問題,跟今天要解決的呈現問題不同維度,但值得先知道它存在。

一份訂單列表如果有五十筆資料,每一列都各自重新查詢一次對應的客戶資料,等於在同一次頁面請求裡對客戶資料表發出五十次獨立查詢,資料筆數一多,這種逐列重複查詢會造成明顯的效能負擔。

Filament 在處理這類關聯欄位時,通常會一次性把用得到的關聯資料準備好再顯示,不會真的讓每一列各自發一次請求。這裡只需要讀者知道這個問題存在、也已經被處理,不展開查詢層面的細節,效能議題我們留給系列後面的階段深入。

訂單列表加上客戶名稱,第一次看得出這張訂單是誰的

把上面那段程式碼接到訂單 Resource 的表格定義裡,客戶欄位就從一串主鍵數字,變成一眼可讀的店名。原本排成一列的 7、12、3,現在會顯示成「旺旺寵物用品行」「台北市寵物協會」「來來寵物美容」,業務人員掃過整份列表,馬上就能對上每一張訂單背後是誰下的單,不再需要靠記憶力硬背哪個編號對應哪家店。

關聯資料表上可以借用的欄位不只一個。假如業務人員除了知道客戶名稱之外,還想順手看一眼這張訂單屬於哪個地區,方便安排配送順序,只要客戶資料表上已經有這個欄位,同樣用小數點串接的方式再加一欄即可:

TextColumn::make('customer.region')
    ->label('所屬地區')
    ->formatStateUsing(fn (string $state): string => match ($state) {
        'north' => '北部',
        'central' => '中部',
        'south' => '南部',
        'east' => '東部',
    })
    ->badge(),

region 是客戶資料表上原本就存在、於 Day 9 用 Select 限制成四個固定選項的欄位,這裡直接借用同一套對照文字,複用 Day 06:表格欄位怎麼呈現,格式化與縮圖顯示 已經示範過的 formatStateUsing() 搭配 badge() 做法,不重新解釋徽章呈現的機制本身。只要關聯已經建立,要多顯示關聯資料表上的哪個欄位其實是彈性的選擇,不限於一個,客戶名稱、所屬地區,甚至聯絡電話,都可以視畫面需要挑著加。

訂單自身的欄位當然也不會被冷落。訂單狀態這個欄位延續 Day 6 分類徽章化的既有做法,搭配顏色區分待處理、已出貨等狀態,這部分機制在 Day 6 已經拆解過,這裡不重複展開,只需要記住客戶名稱這類文字欄位不需要額外的格式化處理,直接顯示即可,跟訂單狀態這類有限選項的欄位適合徽章化,是兩種不同性質的呈現選擇。

訂單列表頁客戶欄位改版前後對照

依客戶篩選訂單,關聯欄位也能拿來篩選

表格欄位能跨表顯示之後,自然會冒出下一個需求,使用者不只想看得出每張訂單是誰的,還想直接篩出某一家客戶的所有訂單。這件事延續 Day 07:讓大量資料可用,篩選、排序與搜尋 已經建立的篩選機制,只是篩選的對象從訂單自身的欄位,換成跨表的關聯欄位:

use Filament\Tables\Filters\SelectFilter;

SelectFilter::make('customer_id')
    ->label('客戶')
    ->relationship('customer', 'name')
    ->searchable(),

這裡的 relationship('customer', 'name') 跟 Day 9 表單端關聯欄位用的是同一個方法簽名,篩選選項的來源同樣是客戶資料表裡目前存在的客戶清單,這份清單會隨客戶資料增減自動變化,不是寫死在程式碼裡的固定選項。

使用者點開這個篩選下拉選單,選擇「旺旺寵物用品行」,訂單列表立刻只留下這家店的所有訂單,其他客戶的資料整批被排除在畫面之外。

這種篩選情境對應的實際業務需求相當直接。客戶會累積訂單紀錄,業務人員想查詢某個客戶過去所有的訂單歷史,可能是要確認上次叫貨的時間,也可能是要核對這家店近期的訂購頻率,依客戶篩選訂單列表,正是這個需求最直接的介面呈現,不需要業務人員自己在腦中回想或翻找紙本紀錄。

訂單自身的欄位一樣可以疊加排序與篩選,訂單狀態、訂單日期都屬於這一類,做法與 Day 7 已經示範過的完全相同:

use Filament\Tables\Filters\SelectFilter as StatusFilter;

TextColumn::make('ordered_at')
    ->label('訂單日期')
    ->date('Y/m/d')
    ->sortable(),

StatusFilter::make('status')
    ->label('訂單狀態')
    ->options([
        'pending' => '待處理',
        'confirmed' => '已確認',
        'shipped' => '已出貨',
    ]),

這裡不再重複展開 Day 7 已教過的機制細節,只需要點出一件事,訂單自身欄位跟客戶這類關聯欄位,可以在同一份表格上並存篩選。業務人員完全可以同時勾選客戶跟訂單狀態兩個篩選條件,一次看到某家客戶目前所有待處理的訂單,這正是篩選機制疊加使用時最實際的價值。

看得出是誰的訂單了,但看不出訂單裡有什麼

今天做完這一輪調整,訂單列表頁的客戶欄位現在直接顯示客戶名稱,不再是一串主鍵數字,也補上依客戶篩選訂單這類延伸情境,甚至能同時顯示地區徽章、疊加訂單狀態篩選。表單端與表格端的關聯資料呈現,到今天總算都到位了,Day 9 結尾點名的落差,已經被實際補齊。

但這裡要誠實點出一個新的落差。翻開一張訂單的編輯頁或檢視頁,現在知道這張訂單屬於哪個客戶,可是這張訂單實際包含哪些商品、各買了多少數量,還是完全看不出來。Day 09:表單裡選關聯資料,下拉選單背後在做什麼 已經點出訂單目前只能關聯到一個商品欄位這個限制,寵物用品店叫貨通常不會只叫一種東西,這個限制到今天依然沒有被解決。一張訂單底下有多筆商品明細,這種資料該怎麼呈現、怎麼管理,目前完全沒有地方可以處理。

一張訂單底下這種一對多的巢狀資料,該用什麼樣的介面呈現與管理,是接下來要處理的內容。


上一篇
Day 09:表單裡選關聯資料,下拉選單背後在做什麼
下一篇
Day 11:Relation Manager,訂單明細這種巢狀資料怎麼管理
系列文
Laravel Filament 從入門到實戰 共 15 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言