git 4deba2bfca6636d5cdcede3f2068eff3b59c15ce
Service providers (сервис-провайдеры, дословно - «поставщики услуг») занимают центральное место в архитектуре Laravel. Они предназначены для первоначальной загрузки (bootstraping) приложения. Ваше приложение, а также сервисы самого фреймворка загружаются через сервис-провайдеры.
Что конкретно означает термин «первоначальная загрузка» или «bootsraping»? Главным образом это регистрация некоторых вещей - таких как биндинги в IoC-контейнер (фасадов и т.д.), слушателей событий (event listeners), фильтров роутов (route filters) и самих роутов (routes). Сервис-провайдеры - центральное место для конфигурирования вашего приложения.
Если вы откроете файл config/app.php, вы увидите массив providers. В нем перечислены все классы сервис-провайдеров, которые загружаются при старте вашего приложения (конечно, кроме тех, которые являются «отложенными» (deferred), т.е. загружаются по требованию другого сервис-провайдера).
Можно и нужно создавать свои собственные сервис-провайдеры для загрузки и настройки различных частей своего приложения.
Сервис-провайдеры должны расширять (extends) класс Illuminate\Support\ServiceProvider. Это абстрактный класс, который требует, чтобы в наследуемом классе был метод register(). В методе register() вы можете только регистрировать свои классы (bindings) в сервис-контейнере. Слушателей событий (event listeners), роуты и фильтры роутов там регистрировать нельзя.
С помощью Artisan можно легко создать нового провайдера, используя команду make:provider:
php artisan make:provider RiakServiceProvider
Вот так может выглядеть простейший сервис-провайдер:
<?php namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider {
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function($app)
{
return new Connection($app['config']['riak']);
});
}
}
В register() мы регистрируем (bind) как singleton (т.е. класс не будет переинициализироваться после вызова из контейнера) в сервис-контейнере класс работы с базой данных Riak. Если для вас этот код выглядит абракадаброй, не беспокойтесь, работу сервис-контейнера мы рассмотрим позже.
Неймспейс App\Providers, в котором находится этот класс сервис-провайдера - дефолтное место для хранения сервис-провайдеров вашего Laravel-приложения, но вы можете располагать свои сервис-провайдеры где угодно внутри вашей PSR-4 папки (если вы не меняли composer.json, то это папка app).
Когда вызвались методы register() всех сервис-провайдеров приложения, вызывается метод boot() сервис-провайдеров. Там уже можно использовать весь существующий функционал классов фреймворка и вашего приложения - регистрировать слушателей событий, подключать роуты и т.п.
<?php namespace App\Providers;
use Event;
use Illuminate\Support\ServiceProvider;
class EventServiceProvider extends ServiceProvider {
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot(Dispatcher $events)
{
Event::listen('SomeEvent', 'SomeEventHandler');
}
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
//
}
}
Обратите внимание, что сервис-контейнер, вызывая метод boot(), сам внедрит те зависимости, которые вы зададите, в частности, Dispatcher.
use Illuminate\Contracts\Events\Dispatcher;
public function boot(Dispatcher $events)
{
$events->listen('SomeEvent', 'SomeEventHandler');
}
Все сервис-провайдеры регистрируются в файле config/app.php путем добавления в массив providers. Все сервис-провайдеры фреймворка находятся там.
Чтобы зарегистрировать свой сервис-провайдер, добавьте название класса в этот массив:
'providers' => [
// другие сервис-провайдеры
'App\Providers\AppServiceProvider',
],
Если ваш провайдер только регистрирует (bind) классы в сервис-контейнере, то вы можете отложить вызов его метода register() до момента, когда эти классы будут затребованы из сервис-контейнера. Это позволит не дергать файловую систему каждый запрос в попытках загрузить файл с нужным классом с диска.
Для того, чтобы сделать сервис-провайдер отложенным, установите свойство defer в true и определить метод provides(), чтобы фреймворк знал, какие классы биндятся (регистрируются в сервис-контейнере, «связываются») в вашем провайдере.
<?php namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('Riak\Contracts\Connection', function($app)
{
return new Connection($app['config']['riak']);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['Riak\Contracts\Connection'];
}
}
Laravel в процессе запуска собирает данные об отложенных сервис-провайдерах и классах, которые ими регистрируются, и когда в процессе работы приложению понадобится класс Riak\Contracts\Connection, он вызовет метод register() сервис провайдера RiakServiceProvider.