# 服務提供者
## 簡介
服務提供器是所有`Laravel`應用程序的引導中心。你的應用程序,以及 通過服務器引導的`Laravel`核心服務都是通過服務提供器引導。
但是,「引導」是什么意思呢? 通常,我們的可以理解為**注冊**,比如注冊服務容器綁定,事件監聽器,中間件,甚至是路由。服務提供器是配置應用程序的中心。
當你打開`Laravel`的`config/app.php`文件時,你會看到`providers`數組。數組中的內容是應用程序要加載的所有服務提供器的類。當然,其中有很多 「延遲」 提供器,他們并不會在每次請求的時候都加載,只有他們的服務實際被需要時才會加載
本篇你將會學到如何編寫自己的服務提供器,并將其注冊到你的`Laravel`應用程序中
## 編寫服務提供器
所有的服務提供器都會繼承`Illuminate\Support\ServiceProvider`類。 大多服務提供器都包含一個`register`和一個`boot`方法。在`register`方法中, 你只需要**將事物綁定到[服務容器](https://laravel-china.org/docs/laravel/5.7/container)**。而不要嘗試在`register`方法中注冊任何監聽器,路由,或者其他任何功能
使用`Artisan`命令界面,通過`make:provider`命令可以生成一個新的提供器:
~~~php
php artisan make:provider RiakServiceProvider
~~~
### 注冊方法
如上所述,在`register`方法中,你只需要將服務綁定到[服務容器](https://laravel-china.org/docs/laravel/5.7/container)中。而不要嘗試在`register`方法中注冊任何監聽器,路由,或者其他任何功能。否則,你可能會意外地使用到尚未加載的服務提供器提供的服務
讓我們來看一個基礎的服務提供器。在任何服務提供器方法中,你總是通過`$app`屬性來訪問服務容器:
~~~php
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* 在服務容器里注冊
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
~~~
這個服務容器只是定義了一個`register`方法,并且使用該這個方法在服務容器中定義了一個`Riak\Connection`接口。如果你不理解服務容器的工作原理,請查看其[文檔].
#### `bindings`和`singletons`特性
如果你的服務提供器注冊了許多簡單的綁定,你可能想用`bindings`和`singletons`屬性替代手動注冊每個容器綁定。當服務提供器被框架加載時,將自動檢查這些屬性并注冊相應的綁定
~~~php
<?php
namespace App\Providers;
use App\Contracts\ServerProvider;
use App\Contracts\DowntimeNotifier;
use Illuminate\Support\ServiceProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\DigitalOceanServerProvider;
class AppServiceProvider extends ServiceProvider
{
/**
*設定所有的容器綁定的對應關系
*
* @var array
*/
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
/**
* 設定所有的單例模式容器綁定的對應關系
*
* @var array
*/
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
];
}
~~~
### Boot 方法
如果我們要在服務提供者中注冊一個[視圖 composer]該怎么做? 這就需要用到`boot`方法了。**該方法在所有服務提供者被注冊以后才會被調用**, 這就是說我們可以在其中訪問框架已注冊的所有其它服務:
~~~php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* 啟動所有的應用服務
*
* @return void
*/
public function boot()
{
view()->composer('view', function () {
//
});
}
}
~~~
#### Boot 方法的依賴注入
你可以為服務提供者的`boot`方法設置類型提示。[服務容器](https://laravel-china.org/docs/laravel/5.7/container)會自動注入你所需要的依賴。
~~~php
use Illuminate\Contracts\Routing\ResponseFactory;
public function boot(ResponseFactory $response)
{
$response->macro('caps', function ($value) {
//
});
}
~~~
## 注冊服務提供者
所有服務提供者都是通過配置文件`config/app.php`進行注冊。該文件包含了一個列出所有服務提供者名字的`providers`數組,默認情況下,其中列出了所有核心服務提供者,這些服務提供者啟動 Laravel核心組件,比如郵件、隊列、緩存等等。
要注冊提供器,只需要將其添加到數組:
~~~php
'providers' => [
// 其他服務提供者
App\Providers\ComposerServiceProvider::class,
],
~~~
## 延遲服務提供者
如果你的服務提供者**僅**在[服務容器]中注冊,可以選擇延遲加載該綁定直到注冊綁定的服務真的需要時再加載,延遲加載這樣的一個提供者將會提升應用的性能,因為它不會在每次請求時都從文件系統加載。
Laravel 編譯并保存延遲服務提供者提供的所有服務的列表,以及其服務提供者類的名稱。因此,只有當你在嘗試解析其中一項服務時,Laravel 才會加載服務提供者。
要延遲加載一個提供者,設置`defer`屬性為`true`并設置一個`provides`方法。`provides`該方法返回該提供者注冊的服務容器綁定:
~~~php
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
*標記服務提供者是否延遲加載
*
* @var bool
*/
protected $defer = true;
/**
* 注冊服務提供者
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection($app['config']['riak']);
});
}
/**
* 獲取由提供者提供的服務
*
* @return array
*/
public function provides()
{
return [Connection::class];
}
}
~~~