Контейнер внедрения зависимости выступает в качестве фундамента для почти всех остальных возможностей Laravel.
Различные названия для возможности внедрения классов:
Внедрение зависимостей происходит снаружи и контроллеру вообще не обязательно знать, какой это конкретно класс - главное, что в нем есть все необходимые методы для работы - это гарантирует интерфейс, который указывается при типизации аргумента.
Простое внедрение зависимостей
Обычное внедрение зависимости в php через конструктор:
class MailController{ protected $mailer; public function __construct(Mailer $mailer){ $this->mailer = $mailer; } public function welcome($user){ return $this->mailer->mail($user->email, 'Welcome!') } }
Данный класс MailController ожидает, что при его инстанцировании будет внедряться объект типа Mailer, после чего его методы будут ссылаться на этот экземпляр.
Основное преимущество внедрения зависимостей заключается в том, что мы можем менять внедряемые классы:
use App\Mailers\Mailer; use App\Mailers\LocalMailer; #... $controller = new MailController(new LocalMailer); //или $controller = new MailController(new Mailer);
Автоматическе внедрение зависимостей в Laravel
Для внедрения зависимостей можно воспользоваться глобальным хелпером app()
class Bar { public function __construct(){} } class Baz { public function __construct(){} } class Foo { public function __construct(Bar $bar, Baz $baz){} } $foo = app(Foo::class);
Основное преимущество такого способа внедрения - это автоматическое разрешение зависимостей. Хелперу app() не нужно указывать входящие параметры для класса Foo.
Дело в том, что обе зависимости Bar и Baz настолько просты, что контейнер может разрешить их без дополнительной информации. Контейнер читает подсказки типов в конструкторе класса Foo и разрешает экзепляры классов Bar и Baz, а затем внедряет их в новый класс Foo при его создании.
Автоматическое внедрение означет, что если класс не был явно привязан к контейнеру, но контейнер может выяснить, как его следует разрешить, то контейнер разрешит этот класс.
Как следствие, нам требуется выполнить привязку только тех классов, которые содержат неразрешимые (или уникальные) параметры.
Bind
Механизм связывания реализуется с помощью специального метода bind():
$this->app->bind('some', 'App\SomeClass');
Теперь, каждый раз, когда мы будем обращаться к сервис-контейнеру таким образом:
$some = $this->app->make('some');
Он будет возвращать вновь созданный объект класса App\SomeClass.
Если мы хотим, чтобы сервис-контейнер не только возвращал нам объект, но и пресетировал (преднастраивал) его, или если создание объекта класса требует каких-то особых аргументов, мы можем передать вторым параметром не название класса, а замыкание, и описать все необходимые действия:
$this->app->bind('some', function($app){ $some = new \App\SomeClass('argument_1', 'argument_2'); $some->setSomething('example'); return $some; });
Обратите внимание, что замыкание единственным аргументом принимает объект класса Illuminate\Foundation\Application, который и является этим самым сервис-контейнером, и он (сервис-контенер) также доступен через алиас фасада App, хелпер app(), а также как $this->app во многих классах Laravel. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например, так:
$this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); });
Singleton
Следующий пример делает точно то же самое, что и предыдущий, однако объект класса будет создан однажды - при первом вызове, а все последующие обращения к тому же контейнеру будут возвращать созданный ранее объект:
$this->app->singleton('some', 'App\SomeClass');