想從套件增加通知的傳送方式時,請將頻道的選擇與收件目標的決定分開設計。本頁以一個將維運事件寫入日誌的小型頻道為例,說明套件所提供類別的解析路徑,以及更新時需要進行的驗證。即使改為傳送到外部服務,頻道與通知之間的職責分工也相同。
本頁內容依據對 Laravel Framework v13.38.0 實作的確認。通知的基本操作請參閱通知,套件的測試環境請參閱套件測試。
類別名稱代表傳送方式,路由代表收件目標
via() 回傳的頻道類別名稱,用來指定要呼叫哪個物件的 send()。而從 routeNotificationFor() 取得的值,則是該頻道所使用的收件目標。兩者不需要使用相同的名稱。
NotificationSender 會將 via() 選出的頻道名稱傳給 ChannelManager::driver()。當 ChannelManager 在內建名稱與已註冊名稱中都找不到,且該名稱是實際存在的類別時,就會透過容器解析該類別。若只以類別名稱使用,就不需要 Notification::extend() 或套件的服務提供者。你只需確保 Composer 的自動載入能找到該類別,並且容器能解析建構子的相依項目。
由套件提供頻道本體
範例中的 Acme\OperationsLog 套件,會將不含機密資訊的事件名稱,連同收件目標的維運團隊名稱一起記錄。在 Laravel 應用程式中,Psr\Log\LoggerInterface 會繫結到日誌服務,因此可以從建構子接收。
接收的物件預期是使用 Notifiable trait 的模型,或由 Notification::route() 建立的隨需通知目標。前者會回傳 routeNotificationForOperationsLog() 的值,後者則回傳 route('operationsLog', ...) 註冊的值。為了避免在沒有收件目標的情況下被當作已傳送,此範例會擲出例外。即使改為實際的外部傳送,也請確認收件目標與傳送結果,並設計成不會把失敗吞掉當作成功。
請勿在寫入日誌的事件名稱或收件目標中包含存取權杖或個人資訊。此範例是用來示範頻道解析與測試的日誌傳送,並非傳送到外部服務的實作。
在使用端定義通知與收件目標
應用程式的通知會回傳頻道類別,並只定義要傳給頻道的訊息。使用 ShouldQueue 與 Queueable 後,一般的 notify() 呼叫就會經由佇列處理。
若要當場指定收件目標,Notification::route() 的鍵不是頻道類別名稱,而是頻道內呼叫的 routeNotificationFor('operationsLog', ...) 所使用的鍵。
若要從應用程式的模型傳送,請在使用 Notifiable 的模型中準備以下方法。
對此模型呼叫 $user->notify(new HealthCheckPassed()) 時,也會選用相同的頻道。請設定佇列連線,並啟動處理 operations-log 佇列的 worker。
OperationsLogChannel 本身會依頻道保留在 Manager 中,因此請勿將收件目標或通知內容儲存在頻道的屬性中,而是每次都從 send() 的參數取出。這樣即使是長時間執行的佇列 worker,也不會沿用前一則通知的資訊。
需要簡短別名時
只有在想讓使用者於 via() 中指定簡短名稱而非類別名稱時,才在套件的一般服務提供者中使用 Notification::extend()。
請透過套件自動探索等方式,讓提供者在傳送處理開始前載入。在選用別名的通知中,將 via() 改為 ['acme-operations-log'],viaQueues() 的鍵也改為相同名稱。用於取得收件目標的 operationsLog 是另一個鍵,因此不需要變更。請使用不會與內建頻道名稱衝突的名稱。
Manager 會依名稱保留已解析的頻道。即使在同一個行程中重新註冊別名的建立處理,已建立的實例也不會被取代。變更註冊方式時,也請在新啟動的應用程式或 worker 中進行驗證。
在 fake 之外另行測試傳送處理
Notification::fake() 是用來驗證傳送通知的意圖。它無法確認頻道的 send() 或路由的取得是否正常運作。測試傳送時,請使用真正的通知 Manager,並將 LoggerInterface 替換為測試用的實作。
以下是從 Testbench 的 TestCase 執行的範例。使用 notifyNow() 時,即使是 ShouldQueue 通知,也能在不啟動 worker 的情況下執行頻道的處理。
此外,也請確認在沒有收件目標時會擲出例外,以及在提供別名時能從全新啟動的環境中解析。若經由佇列使用,也請另外在執行佇列 worker 的驗證用應用程式中,測試任務是否送達 operations-log 佇列並被處理。
NotificationSender 會將 ShouldQueue 通知依頻道拆分為任務,並以 viaQueues() 的鍵選擇佇列。當頻道的 send() 擲出例外時,會觸發 NotificationFailed 並重新擲出例外;正常返回時則觸發 NotificationSent。佇列的重試設定依應用程式的設定而定。請注意,若在頻道內吞掉例外,即使傳送失敗也會被當作成功處理。
參考的一手資料