Подписывайтесь на наш Telegram канал и будьте в курсе всех событий.
Поделитесь своим кодом и идеями!
Поделитесь своим кодом и идеями!

Сервис-провайдеры

Введение

Сервис-провайдеры – это центральное место начальной загрузки всех приложений Laravel. Ваше собственное приложение, а также все основные службы и сервисы Laravel загружаются через них.

Но, что мы подразумеваем под «начальной загрузкой»? В общем, мы имеем в виду регистрацию элементов, включая регистрацию связываний контейнера служб (service container), слушателей событий (event listener), посредников (middleware) и даже маршрутов (route). Сервис-провайдеры являются центральным местом для конфигурирования приложения.

Если вы откроете файл config/app.php, включенный в Laravel, вы увидите массив 'providers'. Это все классы сервис-провайдеров, которые будут загружены вашим приложением. По умолчанию в этом массиве перечислены основные сервис-провайдеры Laravel. Они загружают основные компоненты Laravel, такие, как подсистема отправки почты, очередь, кеш и другие. Многие из этих провайдеров являются «отложенными», что означает, что они не будут загружаться при каждом запросе, а только тогда, когда предоставляемые ими службы действительно необходимы.

В этой документации вы узнаете, как писать собственные сервис-провайдеры и регистрировать их в приложении Laravel.

Если вы хотите узнать больше о том, как Laravel обрабатывает запросы и работает изнутри, ознакомьтесь с нашей документацией по жизненному циклу запроса Laravel.

Написание сервис-провайдеров

Все сервис-провайдеры расширяют класс Illuminate\Support\ServiceProvider. Большинство сервис-провайдеров содержат метод register и boot. В рамках метода register следует только связывать (bind) сущности в контейнере служб. Никогда не следует пытаться зарегистрировать каких-либо слушателей событий, маршруты или что-то другое в методе register.

Чтобы сгенерировать новый сервис-провайдер, используйте команду make:provider Artisan:

php artisan make:provider RiakServiceProvider

Метод register

Как упоминалось ранее, в рамках метода register следует только связывать сущности в контейнере служб. Никогда не следует пытаться зарегистрировать слушателей событий, маршруты или что-то другое в методе register. В противном случае вы можете случайно воспользоваться подсистемой, чей сервис-провайдер еще не загружен.

Давайте взглянем на рядовой сервис-провайдер приложения. В любом из методов сервис-провайдера у вас всегда есть доступ к свойству $app, которое обеспечивает доступ к контейнеру служб:

<?php

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * Регистрация любых служб приложения.
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
            return new Connection(config('riak'));
        });
    }
}

Этот сервис-провайдер определяет только метод register и использует этот метод для указания, какая именно реализация App\Services\Riak\Connection будет применена в нашем приложении – при помощи контейнера служб. Если вы еще не знакомы с контейнером служб Laravel, ознакомьтесь с его документацией.

Свойства bindings и singletons

Если ваш сервис-провайдер регистрирует много простых связываний, вы можете использовать свойства bindings и singletons вместо ручной регистрации каждого связывания контейнера. Когда сервис-провайдер загружается фреймворком, он автоматически проверяет эти свойства и регистрирует их связывания:

<?php

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Все связывания контейнера, которые должны быть зарегистрированы.
     *
     * @var array
     */
    public $bindings = [
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    /**
     * Все синглтоны контейнера, которые должны быть зарегистрированы.
     *
     * @var array
     */
    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerProvider::class => ServerToolsProvider::class,
    ];
}

Метод boot

Итак, что, если нам нужно зарегистрировать компоновщик шаблонов в нашем сервис-провайдере? Это должно быть сделано в рамках метода boot. Этот метод вызывается после регистрации всех остальных сервис-провайдеров, что означает, что в этом месте у вас уже есть доступ ко всем другим службам, которые были зарегистрированы фреймворком:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ComposerServiceProvider extends ServiceProvider
{
    /**
     * Загрузка любых служб приложения.
     */
    public function boot(): void
    {
        View::composer('view', function () {
            // ...
        });
    }
}

Внедрение зависимости в методе boot

Вы можете указывать тип зависимостей в методе boot сервис-провайдера. Контейнер служб автоматически внедрит любые необходимые зависимости:

use Illuminate\Contracts\Routing\ResponseFactory;

/**
 * Загрузка любых служб приложения.
 */
public function boot(ResponseFactory $response): void
{
    $response->macro('serialized', function (mixed $value) {
        // ...
    });
}

Регистрация сервис-провайдеров

Все сервис-провайдеры регистрируются в файле конфигурации config/app.php. Этот файл содержит массив providers, в котором можно перечислить имена классов. По умолчанию, в этом массиве зарегистрирован набор базовых поставщиков сервисов Laravel. Эти поставщики загружают основные компоненты Laravel, такие, как почтовая подсистема, очереди, кеш и другие.

Чтобы зарегистрировать сервис-провайдер, добавьте его в массив:

'providers' => ServiceProvider::defaultProviders()->merge([
    // Другие сервис-провайдеры

    App\Providers\ComposerServiceProvider::class,
])->toArray(),

Отложенные сервис-провайдеры

Если ваш сервис-провайдер регистрирует только связывания в контейнере служб, вы можете отложить его регистрацию до тех пор, пока одно из зарегистрированных связываний не понадобится. Отсрочка загрузки такого сервис-провайдера повысит производительность вашего приложения, так как он не загружается из файловой системы при каждом запросе.

Laravel составляет и сохраняет список всех служб, предоставляемых отложенными сервис-провайдерами, а также имя класса сервис-провайдера. Laravel загрузит сервис-провайдер только при необходимости в одной из этих служб.

Чтобы отложить загрузку сервис-провайдера, реализуйте интерфейс \Illuminate\Contracts\Support\DeferrableProvider, описав метод provides. Метод provides должен вернуть связывания контейнера службы, регистрируемые данным классом:

<?php

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * Регистрация любых служб приложения.
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
            return new Connection($app['config']['riak']);
        });
    }

    /**
     * Получить службы, предоставляемые поставщиком.
     *
     * @return array<int, string>
     */
    public function provides(): array
    {
        return [Connection::class];
    }
}