「這張圖片的檔案在伺服器上到底存在哪裡?」
如果你用過 Spatie 的 Media Library 套件管理檔案上傳,可能已經知道它預設的儲存路徑長什麼樣:一長串隨機產生的 ID 當資料夾名稱,檔案本身埋在裡面。這個設計本身沒有錯——避免路徑衝突、避免可預測性——但當你真的要去伺服器上排查「這個 Model 底下到底有哪些檔案」時,這種路徑結構完全幫不上忙。今天要看的是一個只有十幾行程式碼的類別,怎麼解決這個問題。
PathGenerator 覆寫範例,只改一個方法解決排查困難Media Library 套件預設的路徑生成邏輯,是用每筆媒體紀錄自帶的隨機 ID 當作資料夾名稱。這個設計的好處很直接:不同筆紀錄的路徑天生不會撞在一起,也沒辦法從路徑猜出裡面存的是什麼內容。
但反過來看,這也是它的缺點:如果你要在伺服器的檔案系統裡人工排查「某個特定新聞底下的附件檔案在哪」,光看路徑完全看不出線索——一堆隨機 ID 資料夾,沒有任何一個能一眼看出它屬於哪個 Model、哪一筆紀錄。
getBasePath(),換成人類看得懂的路徑這個系列的素材專案裡,有一個只有十幾行的類別,繼承了套件的預設路徑產生器,只覆寫了其中一個方法——getBasePath()。改寫後的邏輯是:拿到媒體所屬的 Model 類別名稱(轉成小寫),組合 Model 所屬紀錄的 ID、以及這筆媒體紀錄自己的 ID,串成一個新的路徑結構——資料夾長得像「模型名稱 / 該筆資料的 ID / 這筆媒體紀錄的 ID」。
用一組對照來看差異:
❌ 套件預設路徑(一串隨機 ID)
storage/media/a1b2c3d4-.../photo.jpg
→ 從路徑完全看不出這個檔案屬於哪個 Model、哪一筆資料,
要排查只能去資料庫反查
✅ 覆寫 getBasePath(),路徑帶有可讀語意
storage/media/news/42/108/photo.jpg
→ 一眼看出:這是 News 模型、第 42 筆新聞、
第 108 筆媒體紀錄底下的檔案,
伺服器上直接用檔案總管排查也看得懂
Media Library 套件的路徑生成邏輯被拆成了幾個獨立方法(基礎路徑、轉換後圖片的路徑、響應式圖片的路徑),但這幾個方法內部都是呼叫同一個核心方法來組出基礎路徑,再各自往下延伸。這代表只要覆寫這一個核心方法,其他所有依賴它的路徑(縮圖轉換版本、響應式圖片版本)都會自動套用同一套新的路徑規則,不需要每個方法都重寫一次。
這是一個值得記住的套件擴充模式:遇到套件的某個行為想改,不需要整個重寫套件的核心邏輯,先看它有沒有把「你想改的那一小段」拆成一個獨立、可以被覆寫的方法。多數設計良好的套件都會這樣拆分,找到那個最小的覆寫點,比自己從頭實作一套替代方案划算得多。
看到這裡,可能會想到一個問題:如果系統已經上線一段時間、累積了不少用舊路徑規則儲存的檔案,換了新的路徑生成器之後,這些舊檔案怎麼辦——新的程式碼會用新規則去讀取路徑,但檔案實際上還躺在舊路徑底下。
這正是明天要講的主題:一支專門處理這種「換了路徑規則,要把歷史資料安全搬過去」的資料遷移 command,怎麼設計才不會在搬遷過程中弄丟或搬壞任何一筆檔案。
回想你專案裡用到的第三方套件,有沒有哪個套件的預設行為讓你在維運時吃過排查上的苦頭?那個行為,套件本身有沒有提供類似「覆寫某個方法」這樣的擴充點,還是真的只能整套重寫?
getBasePath() 這一個方法,換成「模型名稱/資料 ID/媒體 ID」的可讀路徑結構明天要看另一個真實的演進案例——一個原本寫死在設定檔或程式碼裡的第三方追蹤碼,後來為什麼、又是怎麼變成後台可以直接設定的欄位。