Magento 2: ObjectManager и Proxy-классы

в 18:37, , рубрики: dependency injection, magento 2, proxy, Разработка под e-commerce

Процесс создания объектов в Magento 2 включает в себя некоторые особенности, присущие именно Magento 2 и связанные с автоматической генерацией кода при определенных условиях (proxies, factories и interceptors). С interceptor'ами для меня было все более-менее понятно, а вот назначение двух других типов генерируемых классов было скрыто туманом непонимания. И если относительно фабрик у меня туман пока еще остается, то вот относительно прокси-классов — туман развеялся.

Предположим, что мы делаем команду для выполнения каких-либо действий в режиме CLI. Предположим, что мы используем в этой команде имплементацию интерфейса MagentoQuoteApiCartManagementInterface, которую нам передает в конструктор ObjectManager:

public function __construct(MagentoQuoteApiCartManagementInterface $cartManagement) {}

Мы можем не использовать имплементацию MagentoQuoteApiCartManagementInterface напрямую в нашей команде, эта имплементация может подтягиваться косвенно — наша команда зависит от ClassA, который зависит от ClassB, который зависит от ..., который зависит от MagentoQuoteApiCartManagementInterface.

Тем не менее, если в нашей команде есть прямая или косвенная зависимость от default'ой имплементации интерфейса MagentoQuoteApiCartManagementInterface, то при попытке запустить команду, например:

$ ./bin/magento list

мы вывалимся по ошибке

[MagentoFrameworkExceptionSessionException]                       
Area code not set: Area code must be set before starting a session.

Это происходит потому, что перед началом работы ./bin/magento создает все команды, которые прописаны в etc/di.xml дескрипторах. Для создания нашей команды ObjectManager создает для нее класс MagentoQuoteModelQuoteManagement, имплементирующий интерфейс MagentoQuoteApiCartManagementInterface. В процессе создания зависимостей для этого класса ObjectManager доходит до создания зависимости MagentoFrameworkSessionSessionManager в конструкторе которого есть строка:

$this->start();

и если по каким-то причинам (например, Area code not set) сессия не стартует, то выбрасывается исключение и выполнение CLI-команды аварийно завершается. И совершенно не важно, что мы не собирались использовать сессии при выполнении нашей команды (или не подозревали о том, что создание чего-то там, имплементирующего нужный нам интерфейс, вылетит по причине того, что для создания этого "чего-то там" нужен ресурс, который в данном режиме непроинициирован).

Разработчики Magento 2, столкнувшись с таким некошерным поведением объектов в процессе их создания ObjectManager'ом пришли к простому решению — прокси-классы. Достаточно указать в конструкторе зависимость не от MagentoQuoteApiCartManagementInterface, а от MagentoQuoteApiCartManagementInterfaceProxy, и при создании вашего объекта в него будет передаваться не непосредственная имплементация интерфейса, а класс-декоратор (генерируется автоматически), который и создаст непосредственную имплементацию при первом же использовании какого-либо имплементируемого метода.

Другими словами, достаточно дописать после имени интерфейса или класса Proxy:

public function __construct(MagentoQuoteApiCartManagementInterfaceProxy $cartManagement) {}

как проблема с запуском команды решается (если не решается — почистите нагенеренное: rm -fr ./var/cache/* ./var/generation/*). ClassAProxy, ClassBProxy,…, MagentoQuoteApiCartManagementInterfaceProxy — выбирай уровень по душе.

У Алана Сторма есть хорошая статья "Magento 2 Object Manager: Proxy Objects" (у него вообще, много хороших статей по Magento 2), но из нее мне не совсем было понятно, для чего именно создавались Proxy-декораторы в Magento 2 и какова сфера их применения. Только после того, как я столкнулся с невозможностью перевести свое приложение в production mode

$ ./bin/magento deploy:mode:set production

из-за предательского поведения некоторых конструкторов, я несколько прояснил для себя сферу применения прокси-классов в Magento 2.

Автор: flancer

Источник

Поделиться

* - обязательные к заполнению поля