DI плагины в Magento 2

от автора

В Magento 2 вместо rewrite’ов, использовавшихся в первой версии, появились плагины, которые позволяют переопределить поведение большинства методов, перехватив поток выполнения тремя способами:

  • before
  • after
  • around

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

Задача

Допустим, для конечного пользователя важно, чтобы учет количества продуктов велся с привязкой к конкретному складу. Допустим, в базе создана новая таблица warehouse и учет количества продукта на складе ведется в таблице warehouse_item, где первичным ключом является комбинация идентификатора склада и идентификатора продукта:

CREATE TABLE warehouse_item (   product_id ...,   warehouse_id ...,   qty ...,   PRIMARY KEY (product_id, warehouse_id),   ... )

Т.о., в админке, в гриде продуктов при выводе нужно заместить данные в столбце "Quantity" (cataloginventory_stock_item.qty) их суммарным значением SUM(warehouse_item.qty).


При анализе существующего кода выяснилось, что грид строится на основании данных, предоставляемых Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider, а данные по количеству продукта добавляются в провайдер через настройки DI (magento/module-catalog-inventory/etc/adminhtml/di.xml):

<type name="Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider">     <arguments>         <argument name="addFieldStrategies" xsi:type="array">             <item name="qty" xsi:type="object">Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection</item>         </argument>         ...     </arguments> </type>

Вот код класса AddQuantityFieldToCollection:

namespace Magento\CatalogInventory\Ui\DataProvider\Product; ... class AddQuantityFieldToCollection implements AddFieldToCollectionInterface {     public function addField(Collection $collection, $field, $alias = null)     {         $collection->joinField(             'qty',             'cataloginventory_stock_item',             'qty',             'product_id=entity_id',             '{{table}}.stock_id=1',             'left'         );     } }

Т.е., для того, чтобы метод addField не отрабатывал и не добавлял к коллекции количество продукта из cataloginventory_stock_item, нужно перехватить его выполнение при помощи плагина с использованием способа around.

Создание плагина

Настройка DI

Регистрируем плагин в конфигурации DI нашего модуля (etc/di.xml или etc/adminhtml/di.xml):

<?xml version="1.0"?> <config ...>     <type name="Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection">         <plugin                 name="vendor_module_plugin"                 type="Vendor\Module\Plugin\AddQuantityFieldToCollection"                 sortOrder="100"                 disabled="false"/>     </type> </config>

Код плагина

Для around-перехвата метода addField создаем в плагине метод aroundAddField, который "делает ничего":

namespace Vendor\Module\Plugin;  use Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection as Subject;  class AddQuantityFieldToCollection {     public function aroundAddField(Subject $subject, \Closure $proceed) {         return;     } }

Сгенерированный класс

После очистки генерируемых файлов (./var/generation/*) и перехода чере Admin WebUI в грид продуктов код вновь созданного "перехватчика"" можно найти в файле ./var/generation/Magento/CatalogInventory/Ui/DataProvider/Product/AddQuantityFieldToCollection/Interceptor.php:

<?php namespace Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection;  /**  * Interceptor class for @see  * \Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection  */ class Interceptor extends \Magento\CatalogInventory\Ui\DataProvider\Product\AddQuantityFieldToCollection implements \Magento\Framework\Interception\InterceptorInterface {     use \Magento\Framework\Interception\Interceptor;      public function __construct()     {         $this->___init();     }      /**      * {@inheritdoc}      */     public function addField(\Magento\Framework\Data\Collection $collection, $field, $alias = null)     {         $pluginInfo = $this->pluginList->getNext($this->subjectType, 'addField');         if (!$pluginInfo) {             return parent::addField($collection, $field, $alias);         } else {             return $this->___callPlugins('addField', func_get_args(), $pluginInfo);         }     } }

Стектрейс вызовов


Оранжевым подсвечен класс, сгенерированный Magento 2 (26-я строка — это ___callPlugins после else), белым — наш собственный плагин.

Вывод

Из стектрейса видно, что DI в Magento 2 подменяет классы, для которых заданы плагины, сгенерированным "перехватчиком", который последовательно применяет before, after, around "обертки" для оригинальных методов:

return $this->___callPlugins('addField', func_get_args(), $pluginInfo);

а иногда и сами оригинальные методы:

return parent::addField($collection, $field, $alias);

Ссылки

ссылка на оригинал статьи https://habrahabr.ru/post/279413/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *