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

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


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


public function __construct(\Magento\Quote\Api\CartManagementInterface $cartManagement) {}

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


Тем не менее, если в нашей команде есть прямая или косвенная зависимость от default’ой имплементации интерфейса \Magento\Quote\Api\CartManagementInterface, то при попытке запустить команду, например:


$ ./bin/magento list

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


[Magento\Framework\Exception\SessionException]                       
Area code not set: Area code must be set before starting a session.

Это происходит потому, что перед началом работы ./bin/magento создает все команды, которые прописаны в etc/di.xml дескрипторах. Для создания нашей команды ObjectManager создает для нее класс \Magento\Quote\Model\QuoteManagement, имплементирующий интерфейс \Magento\Quote\Api\CartManagementInterface. В процессе создания зависимостей для этого класса ObjectManager доходит до создания зависимости \Magento\Framework\Session\SessionManager в конструкторе которого есть строка:


$this->start();

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


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


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


public function __construct(\Magento\Quote\Api\CartManagementInterface\Proxy $cartManagement) {}

как проблема с запуском команды решается (если не решается — почистите нагенеренное: rm -fr ./var/cache/* ./var/generation/*). \ClassA\Proxy, ClassB\Proxy, …, \Magento\Quote\Api\CartManagementInterface\Proxy — выбирай уровень по душе.


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


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

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

Комментарии (0)

© Habrahabr.ru