§11 Сервисы и провайдеры сервисов в Laravel 4.2
Сервис-провайдер предназначен для регистрации одного или группы сервисов в IoC контейнере. Сервис-провайдер позволяет добавлять сервисы со смежными обязанностями в одном классе. Это решает проблему увеличения в размере файла start/global.php
и позволяет содержать всю логику сервисов в отдельных классах без лишних инклюдов и с простым механизмом регистрации сервисов в контейнере зависимостей.
Чтобы не путаться в терминологии, сервис - это некий объект или примитив в IoC контейнере приложения, сервис-провайдер - это класс, который управляет регистрацией одного или группы сервисов. Все просто.
Простой способ добавления сервиса
Если вам нужно быстро добавить небольшой сервис - вы можете не использовать класс-провайдер. Но, если вы будете так делать постоянно, то очень вероятно, что со временем логика добавления сервисов станет неуправляемой.
\App::bind('service_name', function(\Illuminate\Foundation\Application $app) {
$cfg1 = $app->config->get('database.default');
$cfg2 = $app['config']->get('database.default');
return new ServiceName($cfg1, $cfg2);
});
Каркас сервис-провайдера
Пример самого простого сервис-провайдера, который добавляет сервис в контейнер:
namespace Providers;
use Illuminate\Support\ServiceProvider;
class MyNewServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('service.first', function() {
return 'некий объект или примитив';
});
}
}
Примечание по именованию
Учитывая, что массив, который содержит набор сервис-провайдеров называется providers
(а не services
), я бы назвал каталог с классами провайдеров одноименно - app/providers/
.
Регистрация провайдера
Укажите полное имя класса сервис-провайдера в секции providers
в конфиге app/config/app.php
:
'providers' => array(
// ...
'Providers\MyNewServiceProvider',
)
При необходимости, вы можете зарегистрировать класс провайдера сервисов "на лету":
\App::register('\Providers\MyNewServiceProvider');
Примечание по автозагрузке
Убедитесь, чтобы автозагрузчик composer
знал о том, что провайдеры сервисов находятся в каталоге app/providers/
. Для этого нужно указать каталог app/providers
в секции autoload.classmap
файла конфигурации composer.json
:
"autoload": {
"classmap": [
...
"app/providers",
]
},
И обновить данные об автозагрузке:
composer dump-autoload
Использование сервиса
Получение сервиса из контейнера:
$serviceFirst = \App::make('service.first');
Расширенные возможности сервис-провайдера
Класс Illuminate\Support\ServiceProvider
, который вы наследуете в провайдере, предоставляет некоторые методы и свойства для управления процессом создания и регистрации сервисов в контейнере. Рассмотрим их назначение и возможные варианты использования.
namespace Providers;
use Illuminate\Support\ServiceProvider;
class SomeNameServiceProvider extends ServiceProvider
{
/**
* Отложенная регистрация сервисов (по необходимости).
* Сервис-провайдер будет зарегистрирован при обращении
* к одному из предоставляемых сервисов, см. метод provides()
*/
protected $defer = false;
/**
* Вызывается единожды перед регистрацией сервис-провайдера.
* Если этому сервис-провайдеру требуется другой сервис, который уже был
* зарегистрирован, или вы хотите перекрыть функционал другого сервиса.
*/
public function boot()
{
$dbCfg = $this->app->config->get('database.default');
$view = $this->app->view;
$otherService = \App::make('other.service');
}
/**
* Абстрактный метод вызывается автоматически в момент регистрации сервис-провайдера
* @return void
*/
public function register()
{
// [!] ВАЖНО [!]
// Не вызывайте другие зависимости непосредственно из тела метода register()!
// Зависимости можно вызывать только внутри анонимных ф-ций или в методе boot().
$this->app->bind('service_name', function() {
$this->app->make('depends_service')->someAction();
return new self($this->app);
});
}
/**
* Возвращает массив предоставляемых сервисов
*/
public function provides()
{
return ['service_name', '<service_other>'];
}
}
Примечание
Обратите внимание, что объект приложения $app
автоматически внедряется в конструкторе наследуемого провайдером класса Illuminate\Support\ServiceProvider.
Способы внедрения сервисов
Рассмотрим основные способы регистрации сервисов внутри метода register()
вашего провайдера.
Регистрация сервиса как синглтона:
$this->app->singleton('service_name', function() {
return 'некий объект или примитив';
});
Регистрация сервиса с помощью метода bindShared():
$this->app->bindShared('service_name', function(\Illuminate\Foundation\Application $app) {
return new ServiceName($app);
});
Регистрация сервиса с помощью метода share():
$this->app['agent'] = $this->app->share(function(\Illuminate\Foundation\Application $app) {
return new Agent;
});
Подробнее о внедрении сервисов (зависимостей) вы можете почитать в статье: принцип работы IoC контейнера.
#service provider, #dependency injection, #DI, #IoC, #dependency resolving