Cache::extend() 的是建立方式,Laravel 並不會自動將回傳的 Store 包裝成 Repository。
本頁確認了 Laravel Framework v13.39.0 的實作。基本操作請參閱快取,提供者的準備請參閱套件開發。
區分驅動名稱與儲存名稱
若設定的
driver 有對應的自訂建立處理,CacheManager::build() 會比內建驅動更優先呼叫它。請不要重複使用 redis 或 array 等名稱,而是使用套件專屬的名稱。
在其他提供者的 boot 之前註冊
若其他套件會在boot() 中使用快取,而你在自己的 boot() 中註冊驅動,依順序不同可能會來不及。請依照官方文件,在 register() 中註冊應用程式的 booting 回呼。
以下是為了在不依賴外部服務的情況下確認註冊路徑,而使用標準 ArrayStore 的最小範例。請放在 Acme\Memo 套件的 src/MemoServiceProvider.php。
- 在一般啟動流程中,應用程式的
booting會在所有提供者的register()完成之後、各個boot()之前執行。這與提供者本身的$this->booting()不同。 extend()的回呼會收到應用程式與儲存設定陣列。Store 的建立與外部連線請放在這個建立用回呼中。- 這個註冊時機並不保證在其他提供者的
register()中使用快取也能運作。請將服務的註冊與實際使用分開。
CacheManager::extend() 會將匿名閉包的作用域重新綁定到 Manager。請不要以為非 static 閉包中的 $this 是提供者,而是像範例一樣使用 static 閉包與引數 $app。
在使用端應用程式中新增儲存
在config/cache.php 既有的 stores 陣列中加入下列內容。請不要將套件設計成會改寫使用端的 default 或整個 stores。
CACHE_STORE 中指定的也是**catalog 這個儲存名稱**,而不是驅動名稱。為同一個驅動建立多組設定時,每個儲存名稱都會建立各自的 Repository。
自行組裝 Repository
CacheManager::callCustomCreator() 會直接回傳回呼的回傳值。若只回傳 Store,即使 get() 與 put() 能運作,也無法使用 Repository 的 remember() 等方法,TTL 的解讀方式也會改變。
範例中的 repository($store, $config) 負責下列事項。
- 以
Illuminate\Cache\Repository包裝 Store。 - 將 Manager 加入設定的
store名稱傳給 Repository。 - 若已註冊事件 Dispatcher,且
events設定未停用,就將 Dispatcher 設定給 Repository。
serialize 設定也是如此。
對照目前的實作確認 Store 契約
請實作 Laravel 13 的Illuminate\Contracts\Cache\Store 所要求的方法。不要將過去範例中的空方法清單直接當成可運作的驅動發布。
null 會被 Repository 視為快取未命中。實作時請不要將 false 或 0 也當成未命中。即使儲存 null,也無法作為防止 remember() 重新計算的標記。
透過 Repository 驗證 TTL
Repository::put() 會在傳給 Store 之前處理 TTL。
add、鎖與標籤需另外確認支援情況
僅實作 Store 契約,並不保證支援所有快取功能。- 原子性的
add():不包含在 Store 契約中。以正的 TTL 呼叫時,若 Store 有add(),Repository 就會使用它。若沒有,則會退回以get()與put()實現,因此僅以 Repository 包裝無法保證原子性。在共享後端中,請以該後端的原子操作實作,並驗證並行執行。 - 鎖:
Illuminate\Contracts\Cache\LockProvider的lock()與restoreLock()是另一個契約。請驗證取得、擁有者一致、過期,以及不會釋放其他擁有者的鎖。範例中ArrayStore的鎖無法提供跨行程的互斥控制。 - 標籤:Repository 會確認 Store 是否有
tags()。一般需要使用TaggableStore等進行實作,僅靠 Store 契約不會自動啟用。
flush() 是會尊重設定中 prefix 的部分刪除。請確認在共享後端中的刪除範圍,並連同 php artisan cache:clear 一起向使用者說明。
區分實例的保留與資料的刪除
CacheManager 是 singleton,store('catalog') 的結果也會依儲存名稱保留。僅變更設定或重新註冊 extend(),並不會更新已建立的 Repository。
$same 與 $before 是同一個物件。在 forgetDriver() 之後取得時,才會依新的設定建立 $after。
forgetDriver() 並不會刪除後端的所有資料。不過,範例中的 ArrayStore 將資料保存在實例內,因此新的 Store 看不到舊資料。此外,使用端的 $before,以及已解析的 cache.store singleton 所保留的舊 Repository 參照,都不會被替換。
在正式環境中,請依照一般的部署流程更新設定快取,並重新啟動長時間執行的 worker。也請考量服務的生命週期,不要將重新解析視為所有行程的一次性更新。
不只模擬 Facade,也要測試建立路徑
在套件的測試環境中,讓處理實際經過 Manager 與 Repository。下面的 Testbench 範例會確認,在先註冊的另一個提供者的boot() 中也能使用該驅動。範例在不變更 cache.serializable_classes 還原限制的情況下儲存陣列,並以 ArrayStore::all(false) 確認原始的儲存值。
many() 的未命中鍵、false 與 0 的往返、touch()、連線故障、並行更新,以及 prefix 與刪除範圍。不要將單純的 Cache::shouldReceive('get') 成功,當成驅動註冊或與外部服務相容的證據。
長期維護的檢查清單
- 將驅動名稱、儲存設定、序列化格式、例外與失敗時的回傳值列為公開規格。
- 依支援的 Laravel 版本,確認 Store 契約的方法,以及標籤、鎖等選用功能。
- 驗證在有設定快取的全新啟動下也能註冊,且能從其他提供者的
boot()中使用。 - 變更儲存資料的相容性時,連同既有鍵的遷移或捨棄方針一併公告。
- 區分行程內的實例保留、後端的資料,以及設定快取。