Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Laravel
Code

Разбираемся в устройстве контейнера IoC Laravel

by
Difficulty:IntermediateLength:MediumLanguages:

Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)

Инверсия контроля, или сокращенно IoC, является техникой, которая позволяет инвертировать контроль выполнения в сравнении с классическим процедурным кодом. Наиболее известная форма IoC - это конечно Инъекция зависимости или DI. Контейнер IoC в Laravel - одна из наиболее часто используемых его возможностей, хотя и возможно менее понимаемая.

Вот простой пример использования Инъекции зависимости, чтобы получить инверсию контроля.

Используя инъекцию через конструктор, мы теперь делегировали создание инстанса Petrol вызывающему объекту, таким образом реализовав инверсию контроля. Наш класс JeepWrangler не должен знать, от куда он получит объект Petrol.

Но какое это все имеет отношение к Laravel? На самом деле очень большое. Laravel, если вы не знали, сам по себе является IoC контейнером. Контейнер это объект, который как вы уже могли догадаться, содержит другие объекты. Контейнер IoC в Laravel используется для хранения множества разных связей. Все что вы делаете в Laravel в какой-то степени взаимодействует с IoC контейнером. Это взаимодействие в большинстве случаев принимает форму получения связей.

Если вы откроете любой из существующих service provider'ов в Laravel, то скорее всего увидите в методе register что-то похоже на это (пример максимально упрощен).

Вот очень, очень простая привязка. Она состоит из имени связи (router) и способа её получения (замыкания). Когда мы пытаемся получить эту связь из контейнера, мы в ответ получим инстанс Router.

Laravel обычно группирует схожие имена связей, например session и session.store.

Чтобы получить связь, мы можем использовать метод контейнера make.

Вот, что контейнер делает в своей самой базовой форме. Но, как и большинство вещей в Laravel, здесь внутри еще есть много всего, помимо привязок и разрешения классов.

Разделяемые и неразделяемые привязки

Если вы просмотрите несколько разных service provider'ов в Laravel, вы заметите, что большинство привязок определены как предыдущем примере. Вот снова:

Эта привязка использует метод share контейнера. Laravel использует статическую переменную для хранения последнего полученного значения и мы просто напросто при разрешении привязки каждый раз его получаем. Вот что в основном то, делает метод share.

Другим способом написания этого будет использование метода bindShared.

Так же можно использовать методы singleton и instance для получения разделяемой привязки. Таким образом если они все делают одно и то же, то в чем же разница? Не большая и разница, на самом деле. Лично я предпочитаю использовать bindShared метод.

Условная привязка

Может быть такое, что вам захочется положить что-нибудь в контейнер, но только если это еще не было в него положено. Есть несколько способов сделать это, наиболее простым является использование метода bindIf.

Этот код поместит в контейнер привязку router только в том случае, если ее еще там нет. Единственное, что нужно здесь знать, это как сделать условную привязку разделяемой. Чтобы реализовать это, следует передать третьим параметром true в метод bindIf.

Автоматическое разрешение зависимостей

Одной из наиболее часто используемых возможностей IoC контейнера является его способность автоматически разрешать зависимости для классов, которые к нему не привязаны. Что это значит? Во-первых, нам на самом деле не нужно что-то класть в контейнер, чтобы разрешить инстанс. Мы можем просто использовать метод make практически для любого класса.

Контейнер автоматически инициализирует класс Petrol для нас. Самой замечательной частью этого является то, что также автоматически для нас будут разрешены все зависимости конструктора.

Первое что делает контейнер, это исследует зависимости класса JeepWrangler. Затем он попытается разрешить эти зависимости. Таким образом, так как наш класс JeepWrangler указывает подсказкой типа на класс Petrol, то контейнер автоматически его создаст и вставит в качестве зависимости.

Контейнер не сможет автоматически вставлять зависимости без подсказок типов. Так, что если одна из наших зависимостей является массивом, то потребуется его инициализировать самим или задать значение по умолчанию.

Привязка реализациями

Возможность в Laravel автоматически разрешать зависимостей является очень удобной и упрощает процесс создания экземпляров классов вручную. Однако есть моменты, когда вам нужна конкретная реализация для инъекции, особенно, при использовании интерфейсов. Этого можно достигнуть используя полное имя класса в качестве привязки. Чтобы это продемонстрировать мы будем использовать новый интерфейс Fuel.

Теперь наш класс JeepWrangler может использовать подсказку типа для интерфейса, и мы можем быть уверены, что наш класс Petrol реализует интерфейс.

Теперь мы может привязать интерфейс Fuel к контейнеру и сделать так, чтобы он разрешался в объект класса Petrol.

Теперь когда мы будем создавать объект JeepWrangler, то контейнер увидит, что объект требует реализацию Fuel, и при этом контейнер знает, что нужно вставить объект класса Petrol.

Это так же дает возможность легко и быстро переключаться между реализациями, так как мы можем просто сменить привязку в контейнере. Чтобы продемонстрировать это, мы можем начать использовать бензин премиум класса, который немного дороже чем обычный.

Контекстные привязки

Обратите внимание, что контекстные привязки стали доступны только с версии Laravel 5.

Контекстная привязка позволяет вам привязать реализацию к определенному классу.

Затем мы создаем новый класс NissanPatrol, который наследуется от абстрактного класса, а затем так же обновляем наш класс JeepWrangler.

В конце мы создаем новый класс Diesel, который реализует интерфейс Fuel.

Сейчас наш Jeep Wrangler будет заправляться используя бензин, а уже Nissan Patrol - дизелем. Если бы мы попробовали использовать те же методы как раньше с привязкой реализации к интерфейсу, то обе машины получили бы одно и тоже топливо, чего мы не хотим.

Так чтобы быть уверенными, что каждая машина заправляется правильным топливом, мы можем сообщить контейнеру, какую реализацию использовать в конкретном контексте.

Теги

Обратите внимание, что теги доступны только с версии Laravel 5.

Способность доставать привязки из контейнера несомненно является очень важной. Обычно мы можем получить что-то из контейнера, только если знаем каким образом это было туда положено. В версии Laravel 5, мы теперь можем добавить тег к привязке, таким образом разработчики смогут получить все привязки по определенному тегу.

Если вы разрабатываете приложение, которое позволяет другим разработчикам создавать плагины, и вы хотите иметь возможность легко получать эти плагины из контейнера, то теги будут чрезвычайно полезны.

Теперь, чтобы получить все привязки по определенному тегу, можно использовать метод tagged.

Пересвязывание

Когда вы что-либо кладете в контейнер с одним и тем же именем несколько раз, это называется пересвязывание. Laravel заметит, что вы снова что-то привязываете и вызовет перевзязывание.

Наибольшая польза от этого станем заметной, когда вы разрабатываете пакет, который позволяет другим разработчикам расширять его с помощью пересвязывания компонентов в контейнере. Чтобы использовать это нам потребуется реализовать инъекцию через сеттер в абстракции Car.

Предположим, что мы привязываем наш класс JeepWrangler к контейнеру следующим образом.

Все отлично, но вдруг еще один разработчик захочет расширить это и использовать премиум бензин в машине. Таким образом используем метод setFuel для инъекции нового топлива в машину.

В большинстве случаев это может быть все, что нужно; однако что если наш пакет становится более сложным и привязки fuel вставляются в несколько других классов? Это приведет к тому, что другому разработчику необходимо будет устанавливать новый объект каждый раз. Таким образом решить эту проблему можно с помощью пересвязывания:

Метод rebinding сразу вернет нам привязанный объект, так что мы сможем использовать его в конструкторе JeepWrangler. Замыкание в методе rebinding получает два параметра, первый - IoC контейнер, а второй - сама привязка. Мы сами можем использовать метод setFuel, чтобы вставить новую привязку в объект JeepWrangler.

Все что нужно теперь другому разработчику, так это перевязать fuel в контейнере. Его service provider возможно будет выглядеть так:

После привязки в контейнере, Laravel автоматически выполнит связанные замыкания. В нашем случае новый объект PremiumPetrol будет установлен в наш JeepWrangler.

Расширение

Если нужно вставить зависимость в одну их корневых привязок или в привязку, созданную пакетом, то в этом поможет метод контейнера extend.

Этот метод достанет привязку из контейнера, а затем выполнит замыкание с контейнером и полученным объектом в качестве параметров. Это позволяет легко получать и встраивать ваши собственные привязки, или просто инициализировать новый класс и вставить его.

В отличие от пересвязывания, это просто установит зависимость с одним связыванием.

Использование вне Laravel

Как и многие Illuminate компоненты, которые составляют основу Laravel, контейнер может использоваться и за пределами Laravel в изолированном приложении. Чтобы сделать это, сначала нужно добавить зависимость в файл composer.json.

Это установит последнюю 4.2 версию контейнера. Теперь все что осталось сделать, это инициализировать контейнер.

Из всех компонентов это один из самых простых для использования везде, где требуется гибкий IoC контейнер.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.