Pull to refresh

DI плагины в Magento 2

Reading time 3 min
Views 13K
В 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);

Ссылки


Tags:
Hubs:
+9
Comments 4
Comments Comments 4

Articles