Введение
Класс-контейнер обратного управления IoC Laravel — мощное средство для управления зависимостями классов. Внедрение зависимостей — это способ исключения вшитых (hardcoded) взаимосвязей классов. Вместо этого зависимости определяются во время выполнения, что даёт бо́льшую гибкость благодаря тому, что они могут быть легко изменены.
Понимание IoC-контейнера Laravel необходимо для построения больших и мощных приложений, так же как и для внесения изменений в код ядра самого фреймворка.
Основы использования
Есть два способа, которыми IoC-контейнер разрешает зависимости: через функцию-замыкание и через автоматическое определение. Для начала мы исследуем замыкания. Первым делом некий «тип» должен быть помещён в контейнер:
App::bind('foo', function ($app) {
return new FooBar;
});
$value = App::make('foo');
При вызове метода PHPApp::make()
вызывается соответствующее замыкание и возвращается результат её вызова.
Помещение «разделяемого» типа в контейнер
Иногда вам может понадобиться поместить в контейнер тип, который должен быть извлечён (создан) только один раз, и чтобы все последующие вызовы возвращали бы тот же объект:
App::singleton('foo', function () {
return new FooBar;
});
Singleton — шаблон проектирования Одиночка — прим. пер.
Помещение готового экземпляра в контейнер
Вы также можете поместить уже созданный экземпляр объекта в контейнер, используя метод PHPinstance()
:
$foo = new Foo;
App::instance('foo', $foo);
Где регистрировать привязки
IoC-привязки, как и обработчики событий или фильтры маршрутов, обычно входят в понятие «код автозагрузки». Другими словами они подготавливают ваше приложение к непосредственной обработке запросов, и, как правило, они должны быть выполнены перед вызовом маршрута или контроллера. Как и для большинства другого кода автозагрузки, IoC-привязки всегда можно зарегистрировать в «стартовых» файлах. Кроме того, вы можете создать файл app/ioc.php (имя файла не имеет значения) и затребовать этот файл из «стартового» файла.
Если в вашем приложении очень большое количество IoC-привязок, или вы просто хотите организовать ваши IoC-привязки в отдельных файлах по категориям, вы можете зарегистрировать привязки в поставщике услуг.
Автоматическое определение
Контейнер IoC достаточно мощен, чтобы во многих случаях определять классы автоматически, без дополнительной настройки. Например:
class FooBar {
public function __construct(Baz $baz)
{
$this->baz = $baz;
}
}
$fooBar = App::make('FooBar');
Обратите внимание, что даже без явного помещения класса PHPFooBar
в контейнер он всё равно был определён и зависимость PHPBaz
была автоматически внедрена в него.
Если тип не был найден в контейнере, IoC будет использовать возможности рефлексии PHP для изучения класса и чтения подсказок типов (type hints) в его конструкторе. С помощью этой информации контейнер сам создаёт экземпляр класса.
Связывание интерфейса и реализации
Однако в некоторых случаях класс может принимать экземпляр интерфейса, а не сам объект. В этом случае нужно использовать метод PHPApp::bind()
для извещения контейнера о том, какая именно зависимость должна быть внедрена:
App::bind('UserRepositoryInterface', 'DbUserRepository');
Теперь посмотрим на следующий контроллер:
class UserController extends BaseController {
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
}
Благодаря тому, что мы связали PHPUserRepositoryInterface
с «настоящим» классом, PHPDbUserRepository
, он будет автоматически встроен в контроллер при его создании.
Практическое использование
Laravel предоставляет несколько возможностей для использования контейнера IoC для повышения гибкости и стабильности вашего приложения. Основной пример — зависимости при использовании контроллеров. Все контроллеры извлекаются из IoC, что позволяет вам использовать зависимости на основе подсказок типов в их конструкторах — ведь они будут определены автоматически.
Подсказки типов для указания зависимостей контроллера
class OrderController extends BaseController {
// OrderRepository - подсказка типа - имя класса передаваемого аргумента-объекта.
public function __construct(OrderRepository $orders)
{
$this->orders = $orders;
}
public function getIndex()
{
$all = $this->orders->all();
return View::make('orders', compact('all'));
}
}
В этом примере класс PHPOrderRepository
автоматически встроится в контроллер. Это значит, что при использовании юнит-тестов класс-заглушка для PHPOrderRepository
может быть добавлен в контейнер, таким образом легко имитируя взаимодействие с БД.
Другие примеры использования IoC
Фильтры, составители и классы-обработчики могут также извлекаться из IoC. При регистрации этих объектов просто передайте имя класса, который должен быть использован:
Route::filter('foo', 'FooFilter');
View::composer('foo', 'FooComposer');
Event::listen('foo', 'FooHandler')
Поставщики услуг
Поставщики услуг (service providers) — отличный способ группировки схожих регистраций в IoC в одном месте. Их можно рассматривать как начальный запуск компонентов вашего приложения. Внутри поставщика услуг вы можете зарегистрировать драйвер авторизации, классы-хранилища вашего приложения или даже собственную команду Artisan.
На самом деле большая часть компонентов Laravel включает поставщиков услуг. Все зарегистрированные поставщики в вашем приложении указаны в массиве providers файла настроек app/config/app.php.
Для создания нового поставщика просто наследуйте класс PHPIlluminate\Support\ServiceProvider
и определите метод PHPregister()
:
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('foo', function () {
return new Foo;
});
}
}
Заметьте, что внутри метода PHPregister()
IoC-контейнер приложения доступен в свойстве PHP$this->app
. Как только вы создали поставщика и готовы зарегистрировать его в своём приложении просто добавьте его в массив providers файла настроек app.php.
Регистрация поставщика услуг во время выполнения
Кроме этого, вы можете зарегистрировать его «на лету», используя метод PHPApp::register()
:
App::register('FooServiceProvider');
События контейнера
Регистрация обработчика события
Контейнер IoC возбуждает событие каждый раз при извлечении объекта. Вы можете отслеживать его с помощью метода PHPresolvingAny()
:
App::resolvingAny(function ($object)
{
//
});
App::resolving('foo', function($foo)
{
//
});