Service Provider
簡介
Service Provider 是 Laravel 中負責啟動應用程式的中心點。不過是你自己開發的應用程式,還是 Laravel 的核心服務,都是使用 Service Provider 啟動的。
不過,「啟動」是什麼意思呢?一般來說,我們指的是 註冊 一些東西,包含註冊 Service Container 的繫結、事件監聽常式、Middleware、甚至是 Route。Service Provider 是用來設定應用程式的中心點。
Laravel uses dozens of service providers internally to bootstrap its core services, such as the mailer, queue, cache, and others. Many of these providers are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed.
All user-defined service providers are registered in the bootstrap/providers.php
file. In the following documentation, you will learn how to write your own service providers and register them with your Laravel application.
若想瞭解 Laravel 如何處理 Request 以及其內部如何運作,請參考我們有關 Laravel Request 的生命週期說明文件。
撰寫 Service Provider
所有的 Service Provider 都繼承自 Illuminate\Support\ServiceProvider
。大多數的 Service Provider 都包含了 registe
與 boot
方法。register
方法 只負責將事物繫結到 Service Container 上。請絕對不要在 register
方法中註冊任何事件監聽常式、Route、或是任何其他的功能。
The Artisan CLI can generate a new provider via the make:provider
command. Laravel will automatically register your new provider in your application's bootstrap/providers.php
file:
1php artisan make:provider RiakServiceProvider
1php artisan make:provider RiakServiceProvider
Register 方法
剛才也提到過,在 register
方法中應只能將東西註冊到 Service Provider 內。絕對不要嘗試在 register
方法內註冊事件監聽常式、Route、或其他任何功能。否則,我們可能會不小心使用到還沒載入的 Service Provider 提供的服務。
來看看一個基礎的 Service Provider。在 Service Provider 中的任何方法都可以存取一個 $app
屬性,該屬性可用來存取 Service Container:
1<?php23namespace App\Providers;45use App\Services\Riak\Connection;6use Illuminate\Contracts\Foundation\Application;7use Illuminate\Support\ServiceProvider;89class RiakServiceProvider extends ServiceProvider10{11 /**12 * Register any application services.13 */14 public function register(): void15 {16 $this->app->singleton(Connection::class, function (Application $app) {17 return new Connection(config('riak'));18 });19 }20}
1<?php23namespace App\Providers;45use App\Services\Riak\Connection;6use Illuminate\Contracts\Foundation\Application;7use Illuminate\Support\ServiceProvider;89class RiakServiceProvider extends ServiceProvider10{11 /**12 * Register any application services.13 */14 public function register(): void15 {16 $this->app->singleton(Connection::class, function (Application $app) {17 return new Connection(config('riak'));18 });19 }20}
這個 Service Provider 只註冊了一個 register
方法,我們使用這個方法來向 Service Container 定義 App\Services\Riak\Connection
的實作。若你不熟悉 Laravel 的 Service Container,請參考 Service Container 的說明文件。
The bindings
and singletons
Properties
若你的 Service Provider 會註冊很多的繫結,則可以使用 bindings
或 singletons
屬性,而不用手動註冊個別的 Container 繫結。Laravel 載入這個 Service Provider 後,會自動檢查這些屬性並註冊這些繫結:
1<?php23namespace App\Providers;45use App\Contracts\DowntimeNotifier;6use App\Contracts\ServerProvider;7use App\Services\DigitalOceanServerProvider;8use App\Services\PingdomDowntimeNotifier;9use App\Services\ServerToolsProvider;10use Illuminate\Support\ServiceProvider;1112class AppServiceProvider extends ServiceProvider13{14 /**15 * All of the container bindings that should be registered.16 *17 * @var array18 */19 public $bindings = [20 ServerProvider::class => DigitalOceanServerProvider::class,21 ];2223 /**24 * All of the container singletons that should be registered.25 *26 * @var array27 */28 public $singletons = [29 DowntimeNotifier::class => PingdomDowntimeNotifier::class,30 ServerProvider::class => ServerToolsProvider::class,31 ];32}
1<?php23namespace App\Providers;45use App\Contracts\DowntimeNotifier;6use App\Contracts\ServerProvider;7use App\Services\DigitalOceanServerProvider;8use App\Services\PingdomDowntimeNotifier;9use App\Services\ServerToolsProvider;10use Illuminate\Support\ServiceProvider;1112class AppServiceProvider extends ServiceProvider13{14 /**15 * All of the container bindings that should be registered.16 *17 * @var array18 */19 public $bindings = [20 ServerProvider::class => DigitalOceanServerProvider::class,21 ];2223 /**24 * All of the container singletons that should be registered.25 *26 * @var array27 */28 public $singletons = [29 DowntimeNotifier::class => PingdomDowntimeNotifier::class,30 ServerProvider::class => ServerToolsProvider::class,31 ];32}
Boot 方法
那麼,若我們想在 Service Provider 內註冊 [View Composer] 該怎麼辦呢?我們可以在 boot
方法中註冊。這個方法會在所有 Service Provider 都註冊好後才被呼叫,這表示,我們就可以存取所有 Laravel 中已註冊好的服務:
1<?php23namespace App\Providers;45use Illuminate\Support\Facades\View;6use Illuminate\Support\ServiceProvider;78class ComposerServiceProvider extends ServiceProvider9{10 /**11 * Bootstrap any application services.12 */13 public function boot(): void14 {15 View::composer('view', function () {16 // ...17 });18 }19}
1<?php23namespace App\Providers;45use Illuminate\Support\Facades\View;6use Illuminate\Support\ServiceProvider;78class ComposerServiceProvider extends ServiceProvider9{10 /**11 * Bootstrap any application services.12 */13 public function boot(): void14 {15 View::composer('view', function () {16 // ...17 });18 }19}
Boot 方法的相依性插入
在 Service Provider 中,若 boot
方法有相依性 (Dependency),我們可以在該方法上做型別提示 (Type-Hint)。Service Container 會自動為你插入所有所需的相依性:
1use Illuminate\Contracts\Routing\ResponseFactory;23/**4 * Bootstrap any application services.5 */6public function boot(ResponseFactory $response): void7{8 $response->macro('serialized', function (mixed $value) {9 // ...10 });11}
1use Illuminate\Contracts\Routing\ResponseFactory;23/**4 * Bootstrap any application services.5 */6public function boot(ResponseFactory $response): void7{8 $response->macro('serialized', function (mixed $value) {9 // ...10 });11}
註冊 Provider
All service providers are registered in the bootstrap/providers.php
configuration file. This file returns an array that contains the class names of your application's service providers:
1<?php23return [4 App\Providers\AppServiceProvider::class,5];
1<?php23return [4 App\Providers\AppServiceProvider::class,5];
When you invoke the make:provider
Artisan command, Laravel will automatically add the generated provider to the bootstrap/providers.php
file. However, if you have manually created the provider class, you should manually add the provider class to the array:
1<?php23return [4 App\Providers\AppServiceProvider::class,5 App\Providers\ComposerServiceProvider::class,6];
1<?php23return [4 App\Providers\AppServiceProvider::class,5 App\Providers\ComposerServiceProvider::class,6];
延遲的 Provider
若 Provider 只有 向 Service Container 註冊繫結,則可以選擇將其註冊過程延遲到真正有需要這些繫結時才註冊。由於我們就不需要每個 Request 都從檔案系統中載入這些 Provider,因此延遲載入這類 Provider 可以提升你程式的效能。
Laravel 會編譯並保存延遲的 Service Provider 名稱、以及其所提供的 Service 列表。接著,當有需要解析其中一個 Service 時,Laravel 就會載入這個 Service Provider:
若要延遲載入 Provider,請實作 \Illuminate\Contracts\Support\DeferrableProvider
介面,並定義 provides
方法。provides
方法應回傳該 Provider 中註冊的 Service Container 繫結:
1<?php23namespace App\Providers;45use App\Services\Riak\Connection;6use Illuminate\Contracts\Foundation\Application;7use Illuminate\Contracts\Support\DeferrableProvider;8use Illuminate\Support\ServiceProvider;910class RiakServiceProvider extends ServiceProvider implements DeferrableProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 $this->app->singleton(Connection::class, function (Application $app) {18 return new Connection($app['config']['riak']);19 });20 }2122 /**23 * Get the services provided by the provider.24 *25 * @return array<int, string>26 */27 public function provides(): array28 {29 return [Connection::class];30 }31}
1<?php23namespace App\Providers;45use App\Services\Riak\Connection;6use Illuminate\Contracts\Foundation\Application;7use Illuminate\Contracts\Support\DeferrableProvider;8use Illuminate\Support\ServiceProvider;910class RiakServiceProvider extends ServiceProvider implements DeferrableProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 $this->app->singleton(Connection::class, function (Application $app) {18 return new Connection($app['config']['riak']);19 });20 }2122 /**23 * Get the services provided by the provider.24 *25 * @return array<int, string>26 */27 public function provides(): array28 {29 return [Connection::class];30 }31}