什麼是 Service Container
Laravel 的 service container 是管理類別依賴並進行依賴注入的機制。所謂依賴注入,是指透過建構子(或有時透過 setter 方法)將類別所需的依賴「注入」到類別中。 看以下例子:PodcastController 需要從 Apple Music 等資料來源取得 Podcast。因此我們注入能取得 Podcast 的服務。透過注入服務,測試時能輕易換成 AppleMusic 服務的 mock(假實作)。
深入理解 service container 對於建構大型 Laravel 應用不可或缺,也對貢獻 Laravel 核心有所幫助。
零設定解析
若類別只依賴其他具體類別(非介面),無需告訴 container 如何解析。例如在routes/web.php 寫下:
此例在路由檔中定義類別僅為示範。實際應用中,service 類別應定義於
app/Services 目錄。Service 類別並注入到路由 handler。無需設定檔即可享受依賴注入。
controller、event listener、middleware 等 Laravel 應用中撰寫的多數類別,其依賴都會透過 container 自動注入。
Binding
基本 binding
多數 binding 會在service provider中登記。在 service provider 內可透過$this->app 屬性存取 container。
bind
用bind 方法傳入類別或介面名稱與 closure 來登記 binding。
App facade。
不依賴介面的類別不需要 bind 到 container。container 會以 reflection 自動解析這些物件。
singleton
singleton 方法會將類別或介面 bind 為只解析一次。一旦解析出的 singleton 之後每次向 container 呼叫都會回傳同一個實例。
singletonIf 方法可以只在該型別尚未登記 binding 時才登記 singleton binding。
Singleton 屬性
也可以在類別或介面加上#[Singleton] 屬性,指示 container 只解析一次。
Scoped singleton binding
scoped 方法將類別或介面 bind 為在 Laravel 請求/job 生命週期內只解析一次。與 singleton 類似,但以 scoped 登記的實例會在 Laravel 應用開始新的「生命週期」時被銷毀,例如 Laravel Octane worker 處理新請求,或 queue worker 處理新 job 時。
scopedIf 方法可以只在該型別尚未登記 binding 時才登記 scoped binding。
Scoped 屬性
也可以在類別或介面加上#[Scoped] 屬性,指示 container 在請求/job 生命週期內只解析一次。
instance
也可用instance 方法將既有物件實例 bind 到 container。之後每次呼叫 container 都會回傳該實例。
將介面 bind 到實作
service container 強大功能之一是可以將介面 bind 到特定實作。例如有EventPusher 介面與 RedisEventPusher 實作:
EventPusher 實作的類別注入 RedisEventPusher。之後只需在建構子 type-hint EventPusher 介面即可。
Bind 屬性
Laravel 還提供更方便的Bind 屬性。在介面加上此屬性,就可以告訴 Laravel 當該介面被要求時要自動注入哪個實作。使用 Bind 屬性時,service provider 中無需再登記。
還可以在介面放多個 Bind 屬性,依環境注入不同實作。
BindWhen 屬性。closure 會收到 container,套用該 binding 時回傳 true。Bind 與 BindWhen 屬性會依宣告順序評估。
使用
BindWhen 屬性需要 PHP 8.5 以上。自動解析(以 type-hint 進行 DI)
service container 在解析 controller、event listener、middleware 等類別時,會查看建構子的 type-hint 自動注入依賴。UserRepository 不依賴介面,就不用登記到 container。只要存取路由,container 就會自動解析依賴並注入到 controller。
從 Container 解析
make 方法
用make 方法可從 container 解析類別實例。
makeWith 方法傳入額外引數。
自動注入
實際上,很少直接呼叫make。只要在 container 會解析的類別(controller、event listener、middleware 等)建構子加上 type-hint,container 就會自動注入。
Facade 與 Container 的關係
Laravel 的 facade 為 container 內的物件提供靜態介面。例如Cache::get() 內部會從 container 取得 Cache 服務再呼叫。
建構子注入的實務範例
看實際應用中的典型模式:1
定義介面
2
建立實作類別
3
在 Service Provider bind
4
在 controller 接收注入
Stripe 換成其他供應商,也只需改一處 binding。
後續步驟
Service Provider
學習使用 service provider 登記 binding 的方式。