Архитектурная концепция Laravel: Сервис Провайдеры
Введение
Но что подразумевается под загрузкой
? В общем, мы имеем в виду регистрацию вещей, включая регистрацию привязок сервис контейнеров, слушателей событий, middleware, и даже маршрутов. Сервис провайдеры являются центральным узлом для настройки вашего приложения.
Если вы откроете файл config/app.php
, входящий в состав Laravel, вы увидите массив providers
. Это всё классы сервис провайдеров, которые будут загружены для вашего приложения. Эти провайдеры загружают основные компоненты Laravel, такие, как почтовая программа, очередь, кэш и прочее. Многие из этих провайдеров являются отложенными
провайдерами, то есть они не будут загружаться при каждом запросе, а только тогда, когда предоставляемые ими услуги действительно необходимы.
В это обзоре вы узнаете, как написать собственные сервис провайдеры и зарегистрировать их в своём Laravel приложении.
Если вы хотите больше узнать о том, как Laravel обрабатывает запросы и работает внутри, ознакомьтесь с нашей документацией по жизненному циклу запросов Laravel.
Написание Сервис Провайдеров
Все сервис провайдеры расширяют класс Illuminate\Support\ServiceProvider
. Большинство сервис провайдеров содержат методы register()
и boot()
. В методе register()
вы должны только привязывать вещие к сервис контейнеру. Вы не должны пытаться зарегистрировать каких либо слушателей событий, маршрутов или любую другую функциональность в методе register()
.
Artisan CLI может генерировать нового провайдера с помощью команды make:provider
:
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
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
View::composer('view', function () {
// ...
});
}
}
Внедрение зависимости метода boot()
Вы можете указывать подсказки типа зависимостей для метода boot()
вашего сервис провайдера. Сервис контейнер автоматически вставит все необходимые вам зависимости:
use Illuminate\Contracts\Routing\ResponseFactory;
/**
* Bootstrap any application services.
*/
public function boot(ResponseFactory $response): void
{
$response->macro('serialized', function (mixed $value) {
// ...
});
}
Регистрация провайдеров
Все сервис провайдеры прописаны в файле config/app.php
. Этот файл содержит массив providers
, в котором вы можете перечислить имена классов ваших сервис провайдеров. По умолчанию в этом массиве перечислены основные сервис провайдеры Laravel. Эти провайдеры загружают основные компоненты Laravel, такие, как почтовая программа, очередь, кэш и прочее.
Для регистрации своего провайдер добавьте его в массив:
'providers' => [
// Other Service Providers
App\Providers\ComposerServiceProvider::class,
],
Отложенные провайдеры
Если ваш провайдер только регистрирует привязки в сервис контейнере, вы можете отложить его регистрацию до тех пор, пока одна из зарегистрированных привязок не понадобиться. Отсрочка загрузки такого провайдера улучшит производительность вашего Laravel приложения, поскольку он не загружается из файловой системы при каждом запросе.
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
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->singleton(Connection::class, function (Application $app) {
return new Connection($app['config']['riak']);
});
}
/**
* Get the services provided by the provider.
*
* @return array<int, string>
*/
public function provides(): array
{
return [Connection::class];
}
}