Last active
May 4, 2022 08:55
Revisions
-
greabock revised this gist
Jan 24, 2017 . 1 changed file with 15 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -18,7 +18,7 @@ Предположим, что у нас есть контроллер. И для его успешной работы ему нужен объект некоего служебного класса (сервиса), который выполнит для него какую-то работу. Для примера, пусть этим служебным классом будет некий хитрый мэйлер, который посылает почту каким-то хитрым образом (сейчас это не важно). Мы без труда можем создать объект мэйлера прямо в методе контроллера. ```php namespace App\Http\Controllers use Request; use Mailers\Mailer; @@ -50,7 +50,7 @@ class MailController extends Controller Теперь предположим, что у нас во многих наших методах используется мэйлер. Логично вынести создание мэйлера в конструктор. ```php namespace App\Http\Controllers use Request; use App\Mailers\Mailer; @@ -91,7 +91,7 @@ class MailController extends Controller namespace App\Mailers\Contracts; interface MailerInterface { public function from(); @@ -190,7 +190,7 @@ $this->app->bind('some', function($app){ return $some; }); ``` Обратите внимание, что замыкание единственным аргументом принимает объект класса `Illuminate\Foundation\Application`, который и является этим самым сервис-контейнером, и он (сервис-контенер) также доступен через алиас фасада `App`, хелпер `app()`, а также как `$this->app` во многих классах Laravel. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например так: ```php $this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); @@ -224,7 +224,16 @@ $this->app->instance('some', $some); ```php $this->app->make('App\SomeClass'); ``` то контейнер просто попытается создать объект этого класса, по возможности разрешая все его зависимости. С фасадами это работает несколько иначе. Если в методе фасада `getFacadeAccessor()` указан класс, который не был помещен в контейнер (методом `bind()` или любым другим) ``` protected static function getFacadeAccessor() { return 'App\SomeClass'; } ``` то контейнер так же попытается создать объект этого класса. Но в этом случае он поместит этот объект в себя, после чего он (объект) будет доступен также как singleton или instance, и все последующие вызовы будут возвращать тот же объект. Отражения == @@ -299,7 +308,7 @@ $this->app->call('<зымыкание>', [#params...]) ``` $this->app->call([$object, 'method'], [#params...]); ``` хотя это и выглядит излишне "замудрёным", на самом деле это не так. Дело в том что `App::call()` - это в действительности обертка над [call_user_func_array](http://php.net/manual/ru/function.call-user-func-array.php). Эта обертка сначала разрешает зависимости, а потом дополняет их аргументами. Кстати говоря, все методы контроллеров так же вызываются через `App::call()`. Поэтому все зависимости указанные в методе-экшене, так же как и в методе-конструкторе - будут, по возможности, разрешены. -
greabock revised this gist
Jun 13, 2015 . No changes.There are no files selected for viewing
-
greabock revised this gist
Jun 13, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -243,7 +243,7 @@ $this->app->make('App\SomeClass'); 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам извлечь информацию о том **что хочет получить** объект, класс, или замыкание. Как это работает === -
greabock revised this gist
Jun 13, 2015 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -240,6 +240,7 @@ $this->app->make('App\SomeClass'); == И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том **что хочет получить** объект, класс, или замыкание. -
greabock revised this gist
Jun 13, 2015 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -240,9 +240,9 @@ $this->app->make('App\SomeClass'); == И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том **что хочет получить** объект, класс, или замыкание. Как это работает === -
greabock revised this gist
Jun 13, 2015 . No changes.There are no files selected for viewing
-
greabock revised this gist
Jun 8, 2015 . 1 changed file with 22 additions and 14 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,10 @@ -------------------------------------------------------------------- > *Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом* - **ru.wikipedia.org** -------------------------------------------------------------------- Сегодня хотелось бы поговорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке, и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: 1. Внедрение зависимостей (Dependency Injection) @@ -120,7 +118,17 @@ class Mailer implements MailerInterface { } ``` После таких манипуляций, наш контроллер может одинаково хорошо работать с любым полученным в конструкторе мэйлером вне зависимости от конкретной его реализации локальной или почтовой. ``` #... public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } #... ``` Внедрение этой зависимости происходит снаружи и контроллеру вообще не обязательно знать какой это конкретно мэйлер - главное, что в нем есть все необходимые методы для работы - это гарантирует интерфейс, который мы указали при типизации аргумента. Обычное внедрение зависимости снаружи класса выглядит так: ```php @@ -137,7 +145,7 @@ $controller = new MailController(new Mailer); ------------ > ***Выжимка:** Внедрение зависимостей в рамках IoC в Laravel, это когда конкретный объект создается не в контексте исполнения (функции или метода класса), а приходит туда извне в виде аргумента. А гарантия доступности публичных методов обеспечивается типизацией интерфейса, абстрактного (или конкретного) класса.* ------------ @@ -174,15 +182,15 @@ $some = $this->app->make('some'); ``` Он будет возвращать вновь созданный объект класса App\SomeClass. Если мы хотим, чтобы сервис-контейнер не только возвращал нам объект, но и пресетировал (преднастраивал) его, или если создание объекта класса требует каких-то особых аргументов - мы можем передать вторым параметром не название класса, а замыкание и описать все необходимые действия: ```php $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. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например так: ```php $this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); @@ -232,9 +240,9 @@ $this->app->make('App\SomeClass'); == И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том **что хочет получить** объект, класс, или замыкание. Как это работает === @@ -252,7 +260,7 @@ $this->make('<Класс зависимости>'); Как это применять === Помните, в самом начале статьи мы говорили про Mailer? и у нас в конструкторе была определена зависимость: ```php @@ -263,11 +271,11 @@ $this->make('<Класс зависимости>'); } #... ``` Так вот, контроллеры в Laravel создаются через этот самый `App::make()`, а это означает что все зависимости будут по возможности разрешены. Ну а так как, в нашем случае, мы используем для типизации интерфейс (и, как мы помним из мануала php, экземпляры интерфейсов и абстрактных классов созданы быть не могут), а не конкретный класс, то нам в сервис провайдере нужно определить разрешение для этой зависимости: ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроллерах, и классах созданных через `App::make()`, где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда-то взбредет в голову поменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что нужно" должно реализовывать MailerInterface. Но нам не всегда нужно или хочется городить интерфейсы. Если нам достаточно получить объект какого-то конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: ```php @@ -290,7 +298,7 @@ $this->app->call('<зымыкание>', [#params...]) ``` $this->app->call([$object, 'method'], [#params...]); ``` хотя это и выглядит излишне "замудрёным", на самом деле это не так. Дело в том что `App::call()` - это в действительности обертка над [call_user_func_array](http://php.net/manual/ru/function.call-user-func-array.php). Эта обертка сначала разрешает зависимости, а потом дополняет их аргументами. По этой причине, зависимости в списке аргументов принято указывать первыми. А все прочие параметры -после них. Тоже самое касается и вызова объектов через `App::make()`. Кстати говоря, все методы контроллеров так же вызываются через `App::call()`. Поэтому все зависимости указанные в методе-экшене, так же как и в методе-конструкторе - будут, по возможности, разрешены. -
greabock revised this gist
May 30, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -137,7 +137,7 @@ $controller = new MailController(new Mailer); ------------ > *Выжимка: Внедрение зависимостей в рамках IoC в Laravel, это когда конкретный объект создается не в контексте исполнения (функции или метода класса), а приходит туда извне в виде аргумента. А гарантия доступности публичных методов обеспечивается типизацией интерфейса, абстрактного (или конкретного) класса.* ------------ -
greabock revised this gist
May 30, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -137,7 +137,7 @@ $controller = new MailController(new Mailer); ------------ > * **Выжимка:** Внедрение зависимостей в рамках IoC в Laravel, это когда конкретный объект создается не в контексте исполнения (функции или метода класса), а приходит туда извне в виде аргумента. А гарантия доступности публичных методов обеспечивается типизацией интерфейса, абстрактного (или конкретного) класса.* ------------ -
greabock revised this gist
May 30, 2015 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -232,9 +232,9 @@ $this->app->make('App\SomeClass'); == И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том **что хочет получить** объект, класс, или замыкание. Как это работает === -
greabock revised this gist
May 30, 2015 . No changes.There are no files selected for viewing
-
greabock revised this gist
May 30, 2015 . 1 changed file with 118 additions and 95 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,22 +1,26 @@ #Понимание IoC -------------------------------------------------------------------- > *Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом* - **ru.wikipedia.org** -------------------------------------------------------------------- Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке, и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: 1. Внедрение зависимостей (Dependency Injection) 2. Сервис-контейнер (ServiceContainer) 3. Отражения (Reflection) Сначала, мы поговорим о каждом из них в отдельности. А потом, о том как они связаны между собой. Внедрение зависимостей == Предположим, что у нас есть контроллер. И для его успешной работы ему нужен объект некоего служебного класса (сервиса), который выполнит для него какую-то работу. Для примера, пусть этим служебным классом будет некий хитрый мэйлер, который посылает почту каким-то хитрым образом (сейчас это не важно). Мы без труда можем создать объект мэйлера прямо в методе контроллера. ```php namepace App\Http\Controllers use Request; use Mailers\Mailer; @@ -31,24 +35,24 @@ class MailController extends Controller { //Создали объект мэйлера $mailer = new Mailer; $mailer ->from(Request::get('sender_id')) ->to(Request::get('receiver_id')) ->subject(Request::get('subject')); $result = $mailer->send(); return Response::json($result); } #... } ``` Теперь предположим, что у нас во многих наших методах используется мэйлер. Логично вынести создание мэйлера в конструктор. ```php namepace App\Http\Controllers use Request; use App\Mailers\Mailer; @@ -64,106 +68,102 @@ class MailController extends Controller //вынесли создание объекта мэйлера в метод-конструктор $this->mailer = new Mailer; } public function sendMail() { $this->mailer ->from(Request::get('sender_id')) ->to(Request::get('receiver_id')) ->subject(Request::get('subject')); $result = $this->mailer->send(); return Response::json($result); } #... } ``` Это круто и классно работает. Ровно до тех пор, пока вам нравится ваш мэйлер. Допустим, что этот мэйлер, организовывал переписку между пользователями посредством электронной почты. И вам внезапно захотелось вести переписку между пользователями локально - в вашем приложении. Тогда, вы заменяете класс `App\Mailers\Mailer` на `App\Mailers\LocalMailer`, но тут есть два "но". Во-первых, нет никаких гарантий, что новый LocalMailer имеет те же публичные методы для работы. Во-вторых, если этот старый мэйлер используется в куче различных контроллеров, то вам придется руками заменить его везде, где он создается. Вот здесь к нам на помощь и приходят интерфейсы и внедрение зависимостей; Для гарантии того, что оба мэйлера имеют одинаковые публичные методы, нужно определить интерфейс которому они должны удовлетворять. ```php namespace App\Mailers\Contracts; class MailerInterface { public function from(); public function to(); public function subject(); public function send(); } ``` и сами мэйлеры должны реализовывать, этот интерфейс: ```php namespace App\Mailers; use App\Mailers\Contracts\MailerInterface; class Mailer implements MailerInterface { #реализация интерфейса } ``` После таких манипуляций, наш контроллер может одинаково хорошо работать с любым полученным в конструкторе мэйлером вне зависимости от конкретной его реализации локальной или почтовой. Внедрение этой зависимости происходит снаружи и контроллеру вообще не обязательно знать какой это конкретно мэйлер - главное, что в нем есть все необходимые методы для работы - это гарантирует интерфейс, который мы указали при типизации аргумента. Обычное внедрение зависимости снаружи класса выглядит так: ```php use App\Mailers\Mailer; use App\Mailers\LocalMailer; #... $controller = new MailController(new LocalMailer); //или $controller = new MailController(new Mailer); ``` Это и называется Dependency Injection. ------------ > ***Выжимка:** Внедрение зависимостей в рамках IoC в Laravel, это когда конкретный объект создается не в контексте исполнения (функции или метода класса), а приходит туда извне в виде аргумента. А гарантия доступности публичных методов обеспечивается типизацией интерфейса, абстрактного (или конкретного) класса.* ------------ Однако Laravel не стал бы столь популярным если бы в нем не было волшебства. Двинемся дальше в поисках магии... Сервис-контейнер == Сервис-контейнер в Laravel решает три основных задачи: 1. Он может хранить информацию о том, как получить данные 2. Он может хранить данные 3. Он может разрешать зависимости ------------ > *Хотя я здесь говорю "данные" ( и это действительно так ), на практике, в контейнере чаще всего хранится объект или информация о том, как его получить (замыкание или имя класса).* ------------ Разберем подробнее(*все примеры приведены так, как если бы они использовались в сервис-провайдере*). Связывание === ```php $this->app->bind('some', 'App\SomeClass'); ``` @@ -172,77 +172,87 @@ $this->app->bind('some', 'App\SomeClass'); ```php $some = $this->app->make('some'); ``` Он будет возвращать вновь созданный объект класса App\SomeClass. Если мы хотим, чтобы сервис-контейнер не только возвращал нам класс, но и пресетировал (преднастраивал) его или если создание объекта класса требует каких-то особых аргументов, мы можем передать вторым параметром не название класса, а замыкание и описать все необходимые действия; ```php $this->app->bind('some', function($app){ $some = new \App\SomeClass('argument_1', 'argument_2'); $some->setSomething('example'); return $some; }); ``` Обратите внимание, что замыкание единственным аргументом принимает принимает объект класса Illuminate\Foundation\Application, который и является этим самым app, который доступе через алиас фасада App, хелпер app(), а также как $this->app во многих классах Laravel. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например так: ```php $this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); }); ``` Одиночки === Следующий пример, делает точно тоже самое что и предыдущий, однако объект класса будет создан однажды - при первом вызове, а все последующие обращения к тому же контейнеру будут возвращать созданный ранее объект: ```php $this->app->singleton('some', 'App\SomeClass'); // true - это один и тот же объект; $check = $this->app->make('some') === $this->app->make('some') ; ``` Инстансы === Связывание инстансов (экземпляров) ведет себя точно так же как и связывание одиночек, с той лишь разницей, что объект в контейнере создается не в момент первого вызова, а еще до помещения в контейнер. ```php $some = new Some; $this->app->instance('some', $some); ``` само собой, здесь нет необходимости в замыканиях, так как всё необходимое пресетирование можно сделать еще до помещения в контейнер. Немного магии === Если попытаться вызвать из контейнера объект, который не был туда помещен, просто по имени класса ```php $this->app->make('App\SomeClass'); ``` то контейнер попытается создать объект этого класса, и в случае успеха поместит этот объект в себя. После чего он (объект) будет доступен также как singleton или instance, и все последующие вызовы будут возвращать тот же объект. Отражения == **(они же Рефлексии, они же Симметрии)** В PHP5 существует набор классов - так называемый Reflection API - который позволяет исследовать существующие классы и собирать их мета-данные. Что это означает? Это означает, что через рефлексии можно получить информацию о методах класса, типизации их аргументов, а также собирать информацию из док-блоков. Я думаю, что многие из вас видели или даже работали с генераторами документации по API приложения. Они работают как раз через исследование рефлексий. В контексте IoC, нас интересует в первую очередь именно типизация аргументов. Я не буду вдаваться в подробности работы с рефлексиями, так как это материал не на одну статью. Если есть желание "расчехлиться", то добро пожаловать в [соответствующий раздел php.net](http://php.net/manual/en/book.reflection.php). На самом деле, я умолчал об одном важном моменте. Вся необходимая работа с рефлексиями уже включена в механизм сервис-контейнера, и самим нам делать с ними ничего не придется. Рассказал я о них лишь для того, чтобы не оставалось белых пятен в понимании того, как контейнер узнаёт о том, *что именно* мы хотим получить в нашем конкретном контроллере или каком-то сервисном классе. Собираем все в кучу, и готовим IoC == И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том с чем мы работаем, а думаем лишь о том как с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том **что хочет получить** объект, класс, или замыкание. Как это работает === Каждый раз, когда мы вызываем из контейнера что-то ```php $this->app->make('App\SomeClass'); ``` для создания чего не было определено замыкания (то есть: он либо был привязан без замыкания, по имнени класса bind/singleton, либо вообще не был помещен в контейнер и вызывается просто по имени класса ), контейнер с помощью рефлексии исследует конструктор этого класса, и попытается разрешить эти зависимости снова вызывая ``` $this->make('<Класс зависимости>'); ``` и так рекурсивно снова и снова, пока все зависимости не будут разрешены. Как это применять === Помните мы в самом начале статьи, мы говорили про Mailer? и у нас в конструкторе была определена зависимость: ```php @@ -253,15 +263,13 @@ $this->app->make('App\SomeClass'); } #... ``` Так вот, контроллеры в Laravel создаются через этот самый `App::make()`, а это означает что все зависимости будут по возможности разрешены. Ну а так как, в нашем случае,е мы используем для типизации интерфейс (и, как мы помним из мануала php, экземпляры интерфейсов и абстрактных классов созданы быть не могут), а не конкретный класс, то нам в сервис провайдере нужно определить разрешение для этой зависимости: ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроллерах, и классах созданных через `App::make()`, где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда взбредет в голову поменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что нужно" должно реализовывать MailerInterface. Но нам не всегда нужно или хочется городить интерфейсы. Если нам достаточно получить объект какого-то конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: ```php #... public function __construct(LocalMailer $mailer) @@ -270,15 +278,30 @@ $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); } #... ``` В этом случае, нам вообще не нужно прописывать привязку в сервис-провайдере. На последок следует сказать, что разрешение зависимостей возможно не только при создании объектов, но и при вызове функций/замыканий/методов. ``` $this->app->call('<зымыкание>', [#params...]) ``` где `замыкание` является или callable/Closure - объектом, или названием статической функции/метода (если это метод, то само собой нужно указасть и класс, включая путь к пространству имен). Если же этот метод должен вызываться в контексте объекта, то первым аргументом передается массив, первым элементом которого является объект, а вторым имя метода ``` $this->app->call([$object, 'method'], [#params...]); ``` хотя это и выглядит излишне "замудрёным", на самом деле это не так. Дело в том что `App::call()` - это в действительности обертка над [call_user_func_array](http://php.net/manual/ru/function.call-user-func-array.php). Которая сначала разрешает зависимости, а потом дополняет их аргументами. По этой причине, зависимости в списке аргументов принято указывать первыми. А все прочие параметры -после них. Тоже самое касается и вызова объектов через `App::make()`. Кстати говоря, все методы контроллеров так же вызываются через `App::call()`. Поэтому все зависимости указанные в методе-экшене, так же как и в методе-конструкторе - будут, по возможности, разрешены. Итог == Мы рассмотрели важные для понимания моменты касающиеся работы IoC.При работе с сервис-контейнером есть множество трюков, которые позволяют легко жонглировать зависимостями. Большинство из них описаны в документации [[en](http://laravel.com/docs/5.0/container)][[ru](http://laravel.su/docs/5.0/container)]. Ну и не стесняйтесь исследовать [сорцы](https://github.com/laravel/framework/blob/5.0/src/Illuminate/Container/Container.php) - там самое интересное. Всем спасибо! Общайтесь в [чатике](https://gitter.im/LaravelRUS/chat), читайте ленту новостей в [группе](http://vk.com/laravel_rus). З.Ы. == Ах, кстати! Сейчас ведется активная работа по переносу проекта KodiCMS (за авторством Павла aka @butschster Бучнева). И пожалуй, что это будет первая адекватная CMS на Laravel (Да простят меня фанаты, но October - это почти полный провал). Так, вот: рукастых и (что не менее важно) мозговитых кодеров, имеющих время и желание помочь - просим присоединятся на [канале](https://gitter.im/KodiCMS/kodicms-laravel), ну а просто любопытных и неравнодушных ждем в [группе](http://vk.com/kodicms). -
greabock revised this gist
May 30, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -39,7 +39,7 @@ class MailController extends Controller $result = $mailer->send(); return Response::json($result); } #... } -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ > Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах[источник не указан 66 дней]. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом. © ***Wikipedia***. Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке, и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: -
greabock revised this gist
May 29, 2015 . 1 changed file with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -151,16 +151,16 @@ $controller = new MailController(new Mailer); ##Сервис-контейнер Сервси-контейнер в Laravel решает три основных задачи: 1. Он может хранить информацию о том, как получить данные 2. Он может хранить данные 3. Он может внедрять зависимости > хотя я здесь говрю "данные" ( и это действительно так ), на практике, в контейнере чаще всего хранится объект или нформация о том, как его получить. Зарзберем подробнее > Все примеры приведены так, как если бы они использовались в сервис-провайдере. ####Связывание -
greabock revised this gist
May 29, 2015 . 1 changed file with 7 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ > Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах[источник не указан 66 дней]. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом. © ***Wikipedia***. Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: @@ -227,14 +227,14 @@ $this->app->make('App\SomeClass'); ##Собираем все в кучу, и готовим IoC И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том **с чем** мыработаем, а думаем лишь о том **как** с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том что **хочет** получить объект или класс. ###Как это работает Каждый раз, когда мы вызываем что-то контейнера ```php $this->app->make('App\SomeClass'); ``` @@ -258,7 +258,7 @@ $this->app->make('App\SomeClass'); ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроеллерах, и классах созданных через App::make() где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда взбредет в голову моменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что-то" должно реализовывать `MailerInterface`. Но нам не всегда нужно или хочется гордить интерфейсы. Если нам достаточно получить объект какогото конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ > Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах[источник не указан 66 дней]. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом. © ***Wikipedia***. Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке, и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -258,7 +258,7 @@ $this->app->make('App\SomeClass'); ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроеллерах, и классах созданных через App::make() где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда-то взбредет в голову поменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что нам нужно" должно реализовывать `MailerInterface`. Но нам не всегда нужно или хочется гордить интерфейсы. Если нам достаточно получить объект какогото конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -258,7 +258,7 @@ $this->app->make('App\SomeClass'); ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроеллерах, и классах созданных через App::make() где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда-то взбредет в голову поменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что-то" должно реализовывать `MailerInterface`. Но нам не всегда нужно или хочется гордить интерфейсы. Если нам достаточно получить объект какогото конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -234,7 +234,7 @@ $this->app->make('App\SomeClass'); ###Как это работает Каждый раз, когда мы вызываем что-то из контейнера ```php $this->app->make('App\SomeClass'); ``` -
greabock revised this gist
May 29, 2015 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -228,9 +228,9 @@ $this->app->make('App\SomeClass'); ##Собираем все в кучу, и готовим IoC И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том **с чем** мыработаем, а думаем лишь о том **как** с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том что **хочет** получить объект или класс. ###Как это работает -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -227,7 +227,7 @@ $this->app->make('App\SomeClass'); ##Собираем все в кучу, и готовим IoC И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том **с чем** мыработаем, а думаем лишь о том **как** с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том что **хочет** получить объект или класс. -
greabock revised this gist
May 29, 2015 . No changes.There are no files selected for viewing
-
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -4,7 +4,7 @@ Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он стоит на трех китах: 1. Внедрение зависимостей (Dependency Injection) 2. Сервис-контейнер (ServiceContainer) -
greabock revised this gist
May 29, 2015 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -78,7 +78,7 @@ class MailController extends Controller } #... } ``` Это круто и классно работает. Ровно до тех пор, пока вам нравится ваш мэйлер; Допустим, что этот мэйлер, организовывал переписку между пользователями посредством электронной почты. И вам внезапно захотелось вести переписку между пользователями локально - в вашем приложении. Тогда, вы заменяете класс `App\Mailers\Mailer` на `App\Mailers\LocalMailer`, но тут есть два "но". Во-первых, нет никаких гарантий, что новый LocalMailer имеет те же публичные методы для работы. Во-вторых, если этот старый мэйлер используется в куче различных контроллеров, то вам придется руками заменить его везде, где он создается. Вот здесь к нам на помощь и приходят итерфейсы и внедрение зависимостей; -
greabock revised this gist
May 29, 2015 . 1 changed file with 19 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,7 +15,7 @@ ##Внедрение зависимостей Предположим, что у нас есть контроллер. И для его успешной работы ему нужен объект некоего служебного класса (сервиса), который выполнит для него какую-то работу. Для примера, пусть этим служебным классом будет некий хитрый мэйлер, который посылает почту каким-то хитрым образом (сейчас это не важно). Мы без труда можем создать объект мэйлера прямо в методе контроллера. ```php <?php namepace App\Http\Controllers use Request; @@ -47,7 +47,7 @@ class MailController extends Controller ``` Теперь предположим, что у нас во многих наших методах используется мэйлер. Логично вынести создание мэйлера в конструктор. ```php <?php namepace App\Http\Controllers use Request; @@ -78,13 +78,13 @@ class MailController extends Controller } #... } ```php Это круто и классно работает. Ровно до тех пор, пока вам нравится ваш мэйлер; Допустим, что этот мэйлер, организовывал переписку между пользователями посредством электронной почты. И вам внезапно захотелось вести переписку между пользователями локально - в вашем приложении. Тогда, вы заменяете класс `App\Mailers\Mailer` на `App\Mailers\LocalMailer`, но тут есть два "но". Во-первых, нет никаких гарантий, что новый LocalMailer имеет те же публичные методы для работы. Во-вторых, если этот старый мэйлер используется в куче различных контроллеров, то вам придется руками заменить его везде, где он создается. Вот здесь к нам на помощь и приходят итерфейсы и внедрение зависимостей; Для гарантии того, что оба мэйлера имеют одинаковые публичные методы, нужно определить интерфейс которому они должны удовлетворять. ```php <?php namespace App\Mailers\Contracts; class MailerInterface { @@ -100,7 +100,7 @@ class MailerInterface { } ``` и сами мэйлеры должны реализовывать, этот интерфейс. ```php <?php namespace App\Mailers; use App\Mailers\Contracts\MailerInterface; @@ -112,7 +112,7 @@ class Mailer implements MailerInterface { Теперь при создании контроллера конкретный мэйлер можно внедрить ```php use Request; use App\Mailers\Mailer; use App\Models\User; @@ -133,7 +133,7 @@ class MailController extends Controller Теперь наш контроллер может одинаково хорошо работать с любым полученым в конструкторе мэйлером вне зависимости от конкретной его реализации локальной или почтовой. Внедрение этой зависимости происходит снаружи и контроллеру вообще не обязательно знать какой это конкретно мэйлер - главное, что в нем есть все необходимые методы для работы - это гарантирует интерфейс, который мы указали при типизации аргумента. Обычное внедрение зависимости снаружи класса выглядит так: ```php use App\Mailers\Mailer; use App\Mailers\LocalMailer; @@ -164,19 +164,19 @@ $controller = new MailController(new Mailer); > Все примеры приведены так, как если бы они использовались в сервис-провайдере. ####Связывание ```php $this->app->bind('some', 'App\SomeClass'); ``` Теперь, каждый раз, когда мы будем обращаться к сервис-контейнеру таким образом: ```php $some = $this->app->make('some'); ``` Он будет возвращать вновь созданный объект класса `App\SomeClass`. Если мы хотим, чтобы сервисконтейнер не только возвращал нам класс, но и пресетировал (преднастраивал) его или если создание объекта класса требует каких-то особых аргументов, мы можем передать вторым параметром не название класса, а замыкание и описать все необходимые действия; ```php $this->app->bind('some', function($app){ $some = new \App\SomeClass('argument_1', 'argument_2'); $some->setSomething('example'); @@ -185,7 +185,7 @@ $this->app->bind('some', function($app){ ``` Обратите внимание, что замыкание единственным аргументом принимает принимает объект класса `Illuminate\Foundation\Application`, который и является этим самым app, который доступе через алиас фасада `App`, хелпер app(), а также как `$this->app` во многих классах Laravel. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например так: ```php $this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); }); @@ -194,7 +194,7 @@ $this->app->bind('some', function($app){ ####Одиночки Следующий пример, делает точно тоже самое что и предыдущий, однако объект класса будет создан однажды - при первом вызове, а все последующие обращения к тому же контейнеру будут возвращать созданный ранее объект: ```php $this->app->singleton('some', 'App\SomeClass'); $check = $this->app->make('some') === $this->app->make('some') ; // true - это один и тот же объект; @@ -204,15 +204,15 @@ $check = $this->app->make('some') === $this->app->make('some') ; // true - эт ####Инстансы Связывание инстансов (экземпляров) вдедет себя точно так же как и связывание одиночек, с той лишь разницей, что объект в контейнере создается не в момент первого вызова, а еще до помещения в контейнер. ```php $some = new Some; $this->app->instance('some', $some); ``` само собой, здесь нет необходимости в замыканиях, так как всё необходимое пресетирование можно сделать еще до помещения в контейнер. ####Немного магии Если попытаться вызвать из контейнера объект, который не был туда помещен, просто по имени класса ```php $this->app->make('App\SomeClass'); ``` то контейнер попытается создать объект этого класса, и в случае успеха поместит этот объект в себя. После чего он (объект) будет доступен также как singleton или instance, и все последующие вызовы будут возвращать тот же объект. @@ -235,7 +235,7 @@ $this->app->make('App\SomeClass'); ###Как это работает Каждый раз, когда мы вызываем что-то контейнера ```php $this->app->make('App\SomeClass'); ``` , для создания чего небыло определено замыкания (то есть: он либо был привязан без замыкания `bind/singleton`, либо вообще не был помещен в контейнер ), контейнер с помощью рефлексии исследует конструктор этого класса, и пытается разрешить эти зависимости снова вызывая $this->app->make('<Класс зависимости>'), и так рекусивно снова и снова, пока все зависимости не будут разрешены. @@ -245,7 +245,7 @@ $this->app->make('App\SomeClass'); Помните мы в самом начеле статьи, мы говорили про Mailer? и у нас в конструкторе была определена зависимость: ```php #... public function __construct(MailerInterface $mailer) { @@ -255,14 +255,14 @@ $this->app->make('App\SomeClass'); ``` Так вот, контрллеры в Laravel создаются через этот самый `App::make()`, а это означает что все зависимости будут по возможности разрешены. Ну а так как, в нашем случа,е мы используем для типизации интерфейс (и, как мы помним из мануала php, экземпляры интерфейсов и абстрактных классов созданы быть не могут), а не конкретный класс, то нам в сервис провайдере нужно определить разрешение для этой зависимости: ```php $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроеллерах, и классах созданных через App::make() где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда взбредет в голову моменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что-то" должно реализовывать `MailerInterface`. Но нам не всегда нужно или хочется гордить интерфейсы. Если нам достаточно получить объект какогото конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: ```php #... public function __construct(LocalMailer $mailer) { @@ -275,7 +275,7 @@ $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); На последок следует сказать, что разрешение зависимостей возможно не только при создании объектов, но и при вызове функций/замыканий/методов. ```php App::call(); ``` -
greabock created this gist
May 29, 2015 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,284 @@ #Понимание IoC > Инверсия управления (англ. Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения зацепления в компьютерных программах[источник не указан 66 дней]. Также архитектурное решение интеграции, упрощающее расширение возможностей системы, при котором контроль над потоком управления программы остаётся за каркасом. © ***Wikipedia***. Сегодня хотелось бы по говорить о реализации инверсии управления в Laravel. Это один из самых важных аспектов организации слабой связанности компонентов в любимом нами фреймворке и его понимание играет ключевую роль при создании качественных пакетов и приложений. Когда мы говорим об IoC в Laravel, то следует знать, что он состоит стоит на трех китах: 1. Внедрение зависимостей (Dependency Injection) 2. Сервис-контейнер (ServiceContainer) 3. Отражения (Reflection) Сначала, мы поговорим каждом из них в отдельности. А потом, о том как они связаны между собой. ##Внедрение зависимостей Предположим, что у нас есть контроллер. И для его успешной работы ему нужен объект некоего служебного класса (сервиса), который выполнит для него какую-то работу. Для примера, пусть этим служебным классом будет некий хитрый мэйлер, который посылает почту каким-то хитрым образом (сейчас это не важно). Мы без труда можем создать объект мэйлера прямо в методе контроллера. ``` <?php namepace App\Http\Controllers use Request; use Mailers\Mailer; use Models\User; use Response use App\Http\Controllers\Controller; class MailController extends Controller { #... public function sendMail() { //Создали объект мэйлера $mailer = new Mailer; $mailer ->from(Request::get('sender_id')) ->to(Request::get('receiver_id')) ->subject(Request::get('subject')); $result = $mailer->send(); return Responce::json($result); } #... } ``` Теперь предположим, что у нас во многих наших методах используется мэйлер. Логично вынести создание мэйлера в конструктор. ``` <?php namepace App\Http\Controllers use Request; use App\Mailers\Mailer; use App\Models\User; use Response use App\Http\Controllers\Controller; class MailController extends Controller { #... public function __construct() { //вынесли создание объекта мэйлера в метод-конструктор $this->mailer = new Mailer; } public function sendMail() { $this->mailer ->from(Request::get('sender_id')) ->to(Request::get('receiver_id')) ->subject(Request::get('subject')); $result = $this->mailer->send(); return Responce::json($result); } #... } ``` Это круто и классно работает. Ровно до тех пор, пока вам нравится ваш мэйлер; Допустим, что этот мэйлер, организовывал переписку между пользователями посредством электронной почты. И вам внезапно захотелось вести переписку между пользователями локально - в вашем приложении. Тогда, вы заменяете класс `App\Mailers\Mailer` на `App\Mailers\LocalMailer`, но тут есть два "но". Во-первых, нет никаких гарантий, что новый LocalMailer имеет те же публичные методы для работы. Во-вторых, если этот старый мэйлер используется в куче различных контроллеров, то вам придется руками заменить его везде, где он создается. Вот здесь к нам на помощь и приходят итерфейсы и внедрение зависимостей; Для гарантии того, что оба мэйлера имеют одинаковые публичные методы, нужно определить интерфейс которому они должны удовлетворять. ``` <?php namespace App\Mailers\Contracts; class MailerInterface { public function from(); public function to(); public function subject(); public function send(); } ``` и сами мэйлеры должны реализовывать, этот интерфейс. ``` <?php namespace App\Mailers; use App\Mailers\Contracts\MailerInterface; class Mailer implements MailerInterface { } ``` Теперь при создании контроллера конкретный мэйлер можно внедрить ``` use Request; use App\Mailers\Mailer; use App\Models\User; use Response use App\Http\Controllers\Controller; class MailController extends Controller { #... public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } #... } ``` Теперь наш контроллер может одинаково хорошо работать с любым полученым в конструкторе мэйлером вне зависимости от конкретной его реализации локальной или почтовой. Внедрение этой зависимости происходит снаружи и контроллеру вообще не обязательно знать какой это конкретно мэйлер - главное, что в нем есть все необходимые методы для работы - это гарантирует интерфейс, который мы указали при типизации аргумента. Обычное внедрение зависимости снаружи класса выглядит так: ``` use App\Mailers\Mailer; use App\Mailers\LocalMailer; #... $controller = new MailController(new LocalMailer); //или $controller = new MailController(new Mailer); ``` Это и называется Dependency Injection. > **Выжимка:**Внедрение зависимостей, это когда конкретный объект создается не в контексте исполнения (функции или метода класса), а приходит туда извне в виде аргумента. А гарантия доступности публичных методов обеспечивается типизацией интерфейса, абстрактного (или конкретного) класса. Однако Laravel не стал бы столь популярным если бы в нем небыло волшебства. Двинемся дальше в поисках магии... ##Сервис-контейнер Сервси-контейнер в Laravel решает две основных задачи: 2. Он может хранить информацию о том, как получить данные; 1. Он может хранить данные; > хотя я здесь говрю "данные" ( и это действительно так ), на практике, в контейнере чаще всего хранится объект или нформация о том, как его получить. Зарзберем каждый пункт подробнее. ###Хранение информации о получении объекта > Все примеры приведены так, как если бы они использовались в сервис-провайдере. ####Связывание ``` $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`, хелпер app(), а также как `$this->app` во многих классах Laravel. Таким образом, внутри замыкания можно вызывать данные из контейнера, которые были определены ранее. Например так: ``` $this->app->bind('some', function($app){ return new \App\SomeClass($app->make('some.else')); }); ``` ####Одиночки Следующий пример, делает точно тоже самое что и предыдущий, однако объект класса будет создан однажды - при первом вызове, а все последующие обращения к тому же контейнеру будут возвращать созданный ранее объект: ``` $this->app->singleton('some', 'App\SomeClass'); $check = $this->app->make('some') === $this->app->make('some') ; // true - это один и тот же объект; ``` ####Инстансы Связывание инстансов (экземпляров) вдедет себя точно так же как и связывание одиночек, с той лишь разницей, что объект в контейнере создается не в момент первого вызова, а еще до помещения в контейнер. ``` $some = new Some; $this->app->instance('some', $some); ``` само собой, здесь нет необходимости в замыканиях, так как всё необходимое пресетирование можно сделать еще до помещения в контейнер. ####Немного магии Если попытаться вызвать из контейнера объект, который не был туда помещен, просто по имени класса ``` $this->app->make('App\SomeClass'); ``` то контейнер попытается создать объект этого класса, и в случае успеха поместит этот объект в себя. После чего он (объект) будет доступен также как singleton или instance, и все последующие вызовы будут возвращать тот же объект. ##Рефлексии (они же Отражения, они же Симметрии) В PHP5 существует набор классов - так называемый Reflection API - который позволяет исследовать существующие классы и собирать их мета-данные. Что это означает? Это означает, что через рефлексии можно получить информаю о методах класса, типизации их аргументов, а также собирать информацию из док-блоков. Я думаю, что многие из вас видели или даже работали с генераторами документации по API приложения. Они работают как раз через исследование рефлексий. В контексте IoC, нас интересует в первую очередь именно типизация аргументов. Я не буду вдаваться в подробности работы с рефлексиями, так как это материал не на одну статью (да и опыта с ними, у меня не так много). Если есть желание "расчехлится", то добро пожаловать в [соответствующий раздел php.net](http://php.net/manual/en/book.reflection.php) На самом деле, я умолчал об одном важном моменте. Вся необходимая работа с рефлексиями уже включена в механизм сервис-контейнера, и самим с ними делать ничего не придется. Упомянул я о них лишь для того, чтобы не оставалось белых пятен в понимании того, как контейнер узнает о том, что мы хотим получить в нашем конкретном контроллере или каком-то сервисном классе. ##Собираем все в кучу, и готовим IoC И так, мы имеем: 1. **Внедрение зависимостей**. Это когда мы не думаем о том **с чем** мыработаем, а думаем лишь о том **как** с этим можно работать. А что именно это будет - определяется вне контекста исполнения. 2. **Сервис контейнер**. Он обеспечивает доступ к объектам, или информации о том, как их правильно создавать. 3. **Рефлексии**. Они позволяют нам получить информацию о том что **хочет** получить объект или класс. ###Как это работает Каждый раз, когда мы вызываем что-то контейнера ``` $this->app->make('App\SomeClass'); ``` , для создания чего небыло определено замыкания (то есть: он либо был привязан без замыкания `bind/singleton`, либо вообще не был помещен в контейнер ), контейнер с помощью рефлексии исследует конструктор этого класса, и пытается разрешить эти зависимости снова вызывая $this->app->make('<Класс зависимости>'), и так рекусивно снова и снова, пока все зависимости не будут разрешены. ###Как это применять Помните мы в самом начеле статьи, мы говорили про Mailer? и у нас в конструкторе была определена зависимость: ``` #... public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } #... ``` Так вот, контрллеры в Laravel создаются через этот самый `App::make()`, а это означает что все зависимости будут по возможности разрешены. Ну а так как, в нашем случа,е мы используем для типизации интерфейс (и, как мы помним из мануала php, экземпляры интерфейсов и абстрактных классов созданы быть не могут), а не конкретный класс, то нам в сервис провайдере нужно определить разрешение для этой зависимости: ``` $this->bind('App\Mailers\Contracts\MailerInterface', 'App\Mailers\LocalMailer'); ``` И теперь во всех контроеллерах, и классах созданных через App::make() где будет внедрен `MailerInterface`, для разрешения зависимости будет поставлен `LocalMailer`. И если нам когда взбредет в голову моменять его на что-то иное, то нам будет достаточно заменить его в нашем сервис-провайдере на то, что нам нужно. Само собой разумеется, что это "что-то" должно реализовывать `MailerInterface`. Но нам не всегда нужно или хочется гордить интерфейсы. Если нам достаточно получить объект какогото конкретного класса, то мы можем просто Типизировать аргумент этим классом,и контейнер попытается создать его через `App::make()`: ``` #... public function __construct(LocalMailer $mailer) { $this->mailer = $mailer; } #... ``` В этом случае, нам вообще не нужно прописывать привязку в сервис-провайдере. На последок следует сказать, что разрешение зависимостей возможно не только при создании объектов, но и при вызове функций/замыканий/методов. ``` App::call(); ``` При работе с сервис-контейнером есть множество трюков, которые позволяют легко жонглировать зависимостями. Большинство из них описаны в документации [en](http://laravel.com/docs/5.0/container), [ru](http://laravel.su/docs/5.0/container). Ну и не стесняйтесь исследовать [сорцы](https://github.com/laravel/framework/blob/5.0/src/Illuminate/Container/Container.php) - там самое интересное. Всем спасибо! шумите в [чатике](https://gitter.im/LaravelRUS/chat), поглядывайте в [ленту в группе](http://vk.com/laravel_rus)