Инъекции зависимостей в Magento 2 – основы их использования
Одной из самых больших изменений в Magento 2 (по сравнению с версиями 1.х) является использование паттерна инъекций зависимостей. Из-за этого шаблона проектирования в базе кода многое было изменено и было введено много новых элементов. Сегодняшняя статья призвана объяснить начинающим саму основу этого шаблона проектирования и использования инъекций зависимостей в Magento 2.
Прежде всего, рассмотрим основную идею использования этого паттерна (шаблона проектирования). Выполняя инъекцию зависимостей, вам нужно спрашивать зависимые ресурсы при создании объекта, вместо создания ресурсов, когда они нужны. Это позволит достичь изоляции класса и независимой разработки, как и простого тестирования из-за легкости сглаживания необходимых объектов. Прежде чем продолжать, позвольте продемонстрировать небольшой пример:
class A { ... public function read() { $dbh = new DatabaseConnection(); // избегайте такого $dbh->query('SELECT ...'); ... } ... }
Предыдущий пример демонстрирует простой класс А с методом read()
. В этом методе нам нужен объект для соединения с базой данных, который создается в первой строке метода. Это то, к чему большинство из нас привыкло, и это несколько похоже на то, что используется в Magento 1.x (фабричный паттерн). Следующий пример реализует ту же логику, но за паттерном инъекции зависимостей:
class A { protected $_dbh; ... public function __construct(DatabaseConnection $connection) { $this->_dbh = $connection; } ... public function read() { $this->_dbh->query('SELECT ...'); ... } ... }
В этом примере объект соединения с базой данных был передан классу А через конструктор, а не создавался в рамках метода read()
. Несмотря на то, что это не единственный способ передачи зависимостей объекта, такая реализация используется в Magento 2. Итак, что же это нам дает?
Класс А может быть разработан независимо от класса DatabaseConnection
, поскольку мы можем всегда подделывать связь для целей разработки. Он не зависит от реализации DatabaseConnection
и таким образом гораздо проще проверить наш код.
Одним из недостатков является то, что второй пример кода чуть больше первого. И это правда, например Magento\Catalog\Model\Product
имеет конструктор, который занимает более 50 строк кода. Кроме того, если вы попытаетесь наладить этот код, вы увидите, что это очень трудная задача. К счастью, здесь на помощь приходит тестирование.
Для инициализации таких крупных объектов инъекции зависимостей имеют контейнер для инъекций зависимостей, а в Magento 2 он называется ObjectManager
. Этот контейнер отвечает за разбор и создание зависимостей после создания объектов. Давайте рассмотрим, как это работает на простом примере:
class A { public function __construct(B $b) { $this->_b = $b; } }
При создании объекта из класса А происходит следующее:
- вызывается
ObjectManager->create('A')
- рассматривается Конструктор А
- создается Класс B и используется для создания A
В этих нескольких шагах ObjectManager создал объект из класса. Но на данный момент не будем считать это таким простым и представим себе, что у нас есть несколько реализаций класса B. Это ставит вопрос, какую реализацию будет использовано? И ответ на этот вопрос можно найти в одном из файлов di.xml
, которые расположены в следующих местах:
{папка_модуля}/etc/(область_кода)/di.xml
{папка_модуля}/etc/di.xml
app/etc/di/*.xml
В этих файлах можно найти/указать следующие параметры
- Определение классов: тип и количество зависимостей. К счастью, Magento 2 использует подпись конструктора для автоматической компиляции
- Конфигурация экземпляра. Определение способа инстанциирования объектов. Это предполагает установку
types
иvirtualTypes
- Разработка карты абстракции-внедрения: это позволяет выбрать нужную имплементацию интерфейса. Это что-то вроде переопределения (
rewrite
) в Magento 1.x.
Еще одна вещь, которую нужно объяснить, — тип объектов для инъекции. В Magento 2 они разделены на две группы: инъекционные и неинъекционные. Сначала рассмотрим неинъекционные объекты. После этого вы сами узнаете инъекционные объекты.
Например, давайте рассмотрим страницу товара, на которой вы хотите отобразить текущий товар и некоторые связанные товары. Если вы передаете модель товара своему контроллеру, для начала вам нужно будет вызвать загрузку этого товара, чтобы получить текущий товар. Но как только этот объект будет использоваться, где вы получите информацию о других товарах? Поэтому здесь мы понимаем, что модель товара – это один из объектов, которые не являются инъекционными. На самом деле, все объекты, которые имеют определенный тип идентичности, могут быть неинъекционными: товары, заказы, элементы корзины, пользователи, …
Чтобы использовать в коде неинъекционные объекты, вы должны подать запрос на их фабрику (factory). Например, если вы пытаетесь загрузить несколько товаров, ваш код будет зависеть от фабрики товара, и через этот объект вы будете вызывать метод create()
с идентификацией товаров, которые вы пытаетесь загрузить. Прокси (proxies) предназначены, главным образом, для отложенной загрузки необязательных или «тяжелых» зависимостей.
Вот мы и рассмотрели коротко основы шаблона проектирования с использованием инъекций зависимостей. Надеемся, что эта информация позволит вам иметь определенный багаж знаний перед более углубленным изучением этой темы. Для этого вы можете ознакомиться со спецификацией на официальном сайте Magento.