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);

    Ссылки


    Поделиться публикацией

    Похожие публикации

    Комментарии 4
      –1
      Спасибо, почитаю на досуге!)
        0
        Пожалуйста. На хабре не любят неинформативные комменты — такова специфика ресурса. Поэтому и минусят.
          –1
          Та ну их. На хабре статьи должны быть информативными. А комментарии для того и есть, что бы писать в них неиформативную чушь или слова благодарности)
            +1
            Вот и выросло новое поколение, которое не помнит настоящего хабра, когда в комментариях было в разы больше полезной информации чем во всей статье.

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

        Самое читаемое