Вступление
Контейнер службы Laravel - это мощный инструмент для управления зависимостями классов и выполнения внедрения зависимостей. Внедрение зависимостей - это причудливая фраза, которая, по сути, означает это: зависимости класса «внедряются» в класс через конструктор или, в некоторых случаях, методы «установки».
Давайте посмотрим на простой пример:
<?php
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
В этом примере UserController
необходимо извлечь пользователей из источника данных. Итак, мы добавим сервис, способный извлекать пользователей. В этом контексте мы, UserRepository
скорее всего, используем Eloquent для извлечения информации о пользователях из базы данных. Однако, поскольку репозиторий внедрен, мы можем легко заменить его другой реализацией. Мы также можем легко «насмехаться» или создавать фиктивную реализацию UserRepository
при тестировании нашего приложения.
Глубокое понимание сервисного контейнера Laravel необходимо для создания мощного, большого приложения, а также для внесения вклада в само ядро Laravel.
Переплет
Обязательные основы
Почти все привязки вашего контейнера услуг будут зарегистрированы у поставщиков услуг , поэтому в большинстве этих примеров будет продемонстрировано использование контейнера в этом контексте.
Нет необходимости связывать классы в контейнер, если они не зависят от каких-либо интерфейсов. Контейнеру не нужно указывать, как создавать эти объекты, поскольку он может автоматически разрешать эти объекты с помощью отражения.
Простые привязки
Внутри поставщика услуг у вас всегда есть доступ к контейнеру через свойство. Мы можем зарегистрировать привязку, используя метод, передавая имя класса или интерфейса, которое мы хотим зарегистрировать, вместе с a, который возвращает экземпляр класса:$this->app
bind
Closure
$this->app->bind('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
Обратите внимание, что мы получаем сам контейнер в качестве аргумента для преобразователя. Затем мы можем использовать контейнер для разрешения зависимостей объекта, который мы строим.
Связывание синглтона
singleton
Метод связывает класс или интерфейс в контейнер , который должен быть разрешен только один раз. После разрешения одноэлементной привязки тот же экземпляр объекта будет возвращен при последующих вызовах в контейнер:
$this->app->singleton('HelpSpot\API', function ($app) {
return new HelpSpot\API($app->make('HttpClient'));
});
Связывающие экземпляры
Вы также можете привязать существующий экземпляр объекта в контейнер, используя instance
метод. Данный экземпляр всегда будет возвращаться при последующих вызовах в контейнер:
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\API', $api);
Связывающие примитивы
Иногда у вас может быть класс, который получает некоторые внедренные классы, но также нуждается в вставленном примитиве, таком как целое число. Вы можете легко использовать контекстную привязку, чтобы ввести любое значение, которое может понадобиться вашему классу:
$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);
Связывание интерфейсов с реализациями
Очень мощная особенность сервисного контейнера - это его способность привязывать интерфейс к данной реализации. Например, давайте предположим, что у нас есть EventPusher
интерфейс и RedisEventPusher
реализация. После того, как мы закодировали нашу RedisEventPusher
реализацию этого интерфейса, мы можем зарегистрировать его в сервисном контейнере следующим образом:
$this->app->bind(
'App\Contracts\EventPusher',
'App\Services\RedisEventPusher'
);
Этот оператор сообщает контейнеру, что он должен внедрить, RedisEventPusher
когда класс нуждается в реализации EventPusher
. Теперь мы можем напечатать EventPusher
интерфейс в конструкторе или в любом другом месте, где зависимости вводятся контейнером службы:
use App\Contracts\EventPusher;
/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}
Контекстная привязка
Иногда у вас может быть два класса, которые используют один и тот же интерфейс, но вы хотите внедрить разные реализации в каждый класс. Например, два контроллера могут зависеть от разных реализаций контракта . Laravel предоставляет простой и удобный интерфейс для определения этого поведения:Illuminate\Contracts\Filesystem\Filesystem
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
Tagging
Иногда вам может потребоваться разрешить все определенные «категории» привязки. Например, возможно, вы создаете агрегатор отчетов, который получает массив различных Report
реализаций интерфейса. После регистрации Report
реализаций вы можете назначить им тег, используя tag
метод:
$this->app->bind('SpeedReport', function () {
//
});
$this->app->bind('MemoryReport', function () {
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
После того, как службы были помечены, вы можете легко разрешить их все с помощью tagged
метода:
$this->app->bind('ReportAggregator', function ($app) {
return new ReportAggregator($app->tagged('reports'));
});
Расширение привязок
extend
Метод позволяет модификацию решенных услуг. Например, когда служба разрешена, вы можете запустить дополнительный код для украшения или настройки службы. extend
Метод принимает Closure, который должен вернуть модифицированную услугу, в качестве единственного аргумента:
$this->app->extend(Service::class, function ($service) {
return new DecoratedService($service);
});
Разрешающая
make
Метод
Вы можете использовать make
метод для разрешения экземпляра класса из контейнера. make
Метод принимает имя класса или интерфейса , который вы хотите , чтобы решить:
$api = $this->app->make('HelpSpot\API');
Если вы находитесь в местоположении вашего кода, который не имеет доступа к $app
переменной, вы можете использовать глобальный resolve
помощник:
$api = resolve('HelpSpot\API');
Если некоторые из зависимостей вашего класса не могут быть разрешены через контейнер, вы можете внедрить их, передав их в виде ассоциативного массива в makeWith
метод:
$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
Автоматический впрыск
В качестве альтернативы и, что важно, вы можете «напечатать подсказку» зависимости в конструкторе класса, который разрешается контейнером, включая контроллеры , прослушиватели событий , промежуточное ПО и многое другое. Кроме того, вы можете вводить зависимости подсказок в handle
методе заданий в очереди . На практике именно так большинство ваших объектов должно быть разрешено контейнером.
Например, вы можете напечатать подсказку, определенную вашим приложением, в конструкторе контроллера. Хранилище будет автоматически разрешено и добавлено в класс:
<?php
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the user with the given ID.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}
Контейнерные События
Служебный контейнер запускает событие каждый раз, когда разрешает объект. Вы можете прослушать это событие, используя resolving
метод:
$this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
});
$this->app->resolving(HelpSpot\API::class, function ($api, $app) {
// Called when container resolves objects of type "HelpSpot\API"...
});
Как видите, разрешаемый объект будет передан в функцию обратного вызова, что позволит вам установить любые дополнительные свойства объекта, прежде чем он будет передан его потребителю.
PSR-11
Сервисный контейнер Laravel реализует интерфейс PSR-11 . Поэтому вы можете напечатать подсказку интерфейса контейнера PSR-11, чтобы получить экземпляр контейнера Laravel:
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
//
});
Исключение выдается, если данный идентификатор не может быть разрешен. Исключением будет случай, если идентификатор никогда не был связан. Если идентификатор был привязан, но не может быть разрешен, будет создан экземпляр .Psr\Container\NotFoundExceptionInterface
Psr\Container\ContainerExceptionInterface
0 комментариев