「Laravel 的用法官方文件不是寫得很清楚嗎?Eloquent、Policy、Job 這些,跟著文件範例做就好,為什麼還需要一個系列講『用法』?」
這是我開始寫這個系列前,設想讀者會問的第一個問題。官方文件的範例確實清楚,但它有一個天生的侷限:範例是為了講清楚「這個功能怎麼用」而設計的,資料表通常只有兩三個欄位,關聯通常只有一層,Job 通常只印一行 log。真實系統不是這樣——一個 Model 身兼多個職責、一份設定要同時應付好幾種執行環境、一個 Job 要處理外部系統回傳的髒資料,這些「文件範例不會告訴你」的複雜度,恰恰是官方文件用法跟真實系統用法之間的落差。
這個系列想做的事很直接:找一套真實運作中、還在持續維護的 Laravel 系統,把它裡面用得上原生框架機制的地方一個一個拆開來看——不是「這個功能怎麼用」的教學文,是「這個真實系統為什麼這樣用、踩過什麼坑、後來怎麼改」的案例記錄。
素材是一個大學官網的入口網站,用 Laravel 12 開發,負責對外呈現新聞公告、單頁內容、輪播、快速連結、視訊頻道這幾類內容,後台則是一套已經運作一段時間、持續有真實使用者在維護內容的系統。它不是為了寫這個系列而生的 demo 專案,程式碼裡累積了將近 600 筆 commit,每一筆背後幾乎都對應到一個真實發生過的需求或 bug。
核心資料模型大致分四類:
這幾類內容背後,都要處理同一組共通的問題:內容怎麼發佈、誰有權限改什麼、怎麼跟外部系統同步資料。這正是這個系列想拆解的三條主線。
這個系統的後台管理介面是用 Filament 開發的,Filament 本身是很好的套件,但這個系列不會展開講 Filament 的用法。原因很單純:Filament 是一層建立在 Laravel 之上的後台框架,它的資源(Resource)、表單、表格背後,終究還是呼叫 Model、Policy、Eloquent 關聯這些 Laravel 原生機制。這個系列想聚焦的正是這一層——不靠套件、靠框架本身能做到什麼——所以碰到「這個類別背後對應到一個 Filament 後台頁面」的情況,會刻意只講底層的 Model/Job/Policy 怎麼寫,Filament 表單怎麼組不會展開。
這個限制帶來一個附加價值:拆掉 Filament 這一層之後,剩下的內容幾乎都是任何 Laravel 專案(不管後台用什麼套件、甚至沒有後台)都用得上的東西。
Model::shouldBeStrict() 抓出的真實 N+1 bug,到一個身兼多職的 Model(疊了樹狀結構、又用動態型別分派行為)這個系列接下來 29 篇,都會回到同一句話:
一個功能「能動」跟「這樣寫是對的」中間,隔著這個系統自己的真實負擔——真正教你怎麼用 Laravel 的,往往不是官方文件的範例,是一個真實系統上一次真實發生過的 bug、一次真實補上的防護、一次事後才想清楚的設計取捨。
官方文件教的是「這個 API 怎麼呼叫」,真實系統教的是「這個 API 該在什麼情境下用、用錯會踩到什麼」。這兩者不衝突,但只讀文件學不到後者——後者只能從真實踩過的案例裡看到。
回想你手上維護的專案,有沒有一段程式碼是「當初照著文件範例寫,後來因為真實情境的複雜度,改了好幾次才變成現在的樣子」?那個改動的過程,你有沒有記錄下來?如果沒有,這個系列接下來的寫法,或許可以當一個參考範本。
明天要具體看 Eloquent 模型設計的一個真實重構故事——一段原本寫在 View 裡的字串處理邏輯,後來為什麼、又是怎麼搬進 Model 的 accessor 裡。
延伸閱讀:本次鐵人賽同時並行的其他系列,會從不同角度處理相關的經驗,有興趣可以一起追: