§11 Сервисы и провайдеры сервисов в Laravel 4.2

Категория: Laravel

Сервис-провайдер предназначен для регистрации одного или группы сервисов в 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

Подробнее об автозагрузке composer.

Использование сервиса

Получение сервиса из контейнера:

$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

категория: Laravel