Service Provider

簡介

Service Provider 是 Laravel 中負責啟動應用程式的中心點。不過是你自己開發的應用程式,還是 Laravel 的核心服務,都是使用 Service Provider 啟動的。

不過,「啟動」是什麼意思呢?一般來說,我們指的是 註冊 一些東西,包含註冊 Service Container 的繫結、事件監聽常式、Middleware、甚至是 Route。Service Provider 是用來設定應用程式的中心點。

若打開 Laravel 中的 config/app.php,可以看到一個 providers 陣列。這個陣列中的就是你的程式會載入的所有 Service Provider。預設情況下,這個陣列中列出了一組 Laravel 的核心 Service Provider。這些 Provider 會啟動 Laravel 的核心元件,如:Mailer、佇列 (Queue)⋯⋯等。其中的許多 Provider 是屬於「延遲的」Provider,延遲的 Provider 是指:不是所有 Request 中都會載入這些 Provider,只有在我們有需要這些 Provider 提供的服務時才會載入。

在本篇概覽中,讀者將可以學習到如何撰寫你自己的 Service Provider,並將這些 Provider 註冊到 Laravel 專案上。

lightbulb

若想瞭解 Laravel 如何處理 Request 以及其內部如何運作,請參考我們有關 Laravel Request 的生命週期說明文件。

撰寫 Service Provider

所有的 Service Provider 都繼承自 Illuminate\Support\ServiceProvider。大多數的 Service Provider 都包含了 registeboot 方法。register 方法 只負責將事物繫結到 Service Container 上。請絕對不要在 register 方法中註冊任何事件監聽常式、Route、或是任何其他的功能。

Artisan CLI 提供了一個 make:provider 指令來新增新 Provider:

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<?php
2 
3namespace App\Providers;
4 
5use App\Services\Riak\Connection;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Support\ServiceProvider;
8 
9class RiakServiceProvider extends ServiceProvider
10{
11 /**
12 * Register any application services.
13 */
14 public function register(): void
15 {
16 $this->app->singleton(Connection::class, function (Application $app) {
17 return new Connection(config('riak'));
18 });
19 }
20}
1<?php
2 
3namespace App\Providers;
4 
5use App\Services\Riak\Connection;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Support\ServiceProvider;
8 
9class RiakServiceProvider extends ServiceProvider
10{
11 /**
12 * Register any application services.
13 */
14 public function register(): void
15 {
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 的說明文件

bindingssingletons 屬性

若你的 Service Provider 會註冊很多的繫結,則可以使用 bindingssingletons 屬性,而不用手動註冊個別的 Container 繫結。Laravel 載入這個 Service Provider 後,會自動檢查這些屬性並註冊這些繫結:

1<?php
2 
3namespace App\Providers;
4 
5use App\Contracts\DowntimeNotifier;
6use App\Contracts\ServerProvider;
7use App\Services\DigitalOceanServerProvider;
8use App\Services\PingdomDowntimeNotifier;
9use App\Services\ServerToolsProvider;
10use Illuminate\Support\ServiceProvider;
11 
12class AppServiceProvider extends ServiceProvider
13{
14 /**
15 * All of the container bindings that should be registered.
16 *
17 * @var array
18 */
19 public $bindings = [
20 ServerProvider::class => DigitalOceanServerProvider::class,
21 ];
22 
23 /**
24 * All of the container singletons that should be registered.
25 *
26 * @var array
27 */
28 public $singletons = [
29 DowntimeNotifier::class => PingdomDowntimeNotifier::class,
30 ServerProvider::class => ServerToolsProvider::class,
31 ];
32}
1<?php
2 
3namespace App\Providers;
4 
5use App\Contracts\DowntimeNotifier;
6use App\Contracts\ServerProvider;
7use App\Services\DigitalOceanServerProvider;
8use App\Services\PingdomDowntimeNotifier;
9use App\Services\ServerToolsProvider;
10use Illuminate\Support\ServiceProvider;
11 
12class AppServiceProvider extends ServiceProvider
13{
14 /**
15 * All of the container bindings that should be registered.
16 *
17 * @var array
18 */
19 public $bindings = [
20 ServerProvider::class => DigitalOceanServerProvider::class,
21 ];
22 
23 /**
24 * All of the container singletons that should be registered.
25 *
26 * @var array
27 */
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<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Support\Facades\View;
6use Illuminate\Support\ServiceProvider;
7 
8class ComposerServiceProvider extends ServiceProvider
9{
10 /**
11 * Bootstrap any application services.
12 */
13 public function boot(): void
14 {
15 View::composer('view', function () {
16 // ...
17 });
18 }
19}
1<?php
2 
3namespace App\Providers;
4 
5use Illuminate\Support\Facades\View;
6use Illuminate\Support\ServiceProvider;
7 
8class ComposerServiceProvider extends ServiceProvider
9{
10 /**
11 * Bootstrap any application services.
12 */
13 public function boot(): void
14 {
15 View::composer('view', function () {
16 // ...
17 });
18 }
19}

Boot 方法的相依性插入

在 Service Provider 中,若 boot 方法有相依性 (Dependency),我們可以在該方法上做型別提示 (Type-Hint)。Service Container 會自動為你插入所有所需的相依性:

1use Illuminate\Contracts\Routing\ResponseFactory;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(ResponseFactory $response): void
7{
8 $response->macro('serialized', function (mixed $value) {
9 // ...
10 });
11}
1use Illuminate\Contracts\Routing\ResponseFactory;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(ResponseFactory $response): void
7{
8 $response->macro('serialized', function (mixed $value) {
9 // ...
10 });
11}

註冊 Provider

config/app.php 中註冊了所有的 Provider。這個檔案中包含了一個 providers 陣列,我們可以在其中列出所有 Service Provider 的類別名稱。預設情況下,這個陣列中已經註冊了一組 Laravel 的核心 Service Provider。預設的這些 Provider 負責啟動 Laravel 的核心元件,如:Mailer、佇列、快取⋯⋯等。

若要註冊 Provider,請將其加到該陣列中:

1'providers' => ServiceProvider::defaultProviders()->merge([
2 // 其他 Service Provider
3 
4 App\Providers\ComposerServiceProvider::class,
5])->toArray(),
1'providers' => ServiceProvider::defaultProviders()->merge([
2 // 其他 Service Provider
3 
4 App\Providers\ComposerServiceProvider::class,
5])->toArray(),

延遲的 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<?php
2 
3namespace App\Providers;
4 
5use App\Services\Riak\Connection;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Contracts\Support\DeferrableProvider;
8use Illuminate\Support\ServiceProvider;
9 
10class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
17 $this->app->singleton(Connection::class, function (Application $app) {
18 return new Connection($app['config']['riak']);
19 });
20 }
21 
22 /**
23 * Get the services provided by the provider.
24 *
25 * @return array<int, string>
26 */
27 public function provides(): array
28 {
29 return [Connection::class];
30 }
31}
1<?php
2 
3namespace App\Providers;
4 
5use App\Services\Riak\Connection;
6use Illuminate\Contracts\Foundation\Application;
7use Illuminate\Contracts\Support\DeferrableProvider;
8use Illuminate\Support\ServiceProvider;
9 
10class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
17 $this->app->singleton(Connection::class, function (Application $app) {
18 return new Connection($app['config']['riak']);
19 });
20 }
21 
22 /**
23 * Get the services provided by the provider.
24 *
25 * @return array<int, string>
26 */
27 public function provides(): array
28 {
29 return [Connection::class];
30 }
31}
翻譯進度
100% 已翻譯
更新時間:
2024年6月30日 上午8:15:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.