DI плагины в Magento 2

в 9:44, , рубрики: customization, magento 2, php, plugins, Разработка под e-commerce

В 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).

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

<type name="MagentoCatalogUiDataProviderProductProductDataProvider">
    <arguments>
        <argument name="addFieldStrategies" xsi:type="array">
            <item name="qty" xsi:type="object">MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection</item>
        </argument>
        ...
    </arguments>
</type>

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

namespace MagentoCatalogInventoryUiDataProviderProduct;
...
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="MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection">
        <plugin
                name="vendor_module_plugin"
                type="VendorModulePluginAddQuantityFieldToCollection"
                sortOrder="100"
                disabled="false"/>
    </type>
</config>

Код плагина

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

namespace VendorModulePlugin;

use MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection 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 MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection;

/**
 * Interceptor class for @see
 * MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection
 */
class Interceptor extends MagentoCatalogInventoryUiDataProviderProductAddQuantityFieldToCollection implements MagentoFrameworkInterceptionInterceptorInterface
{
    use MagentoFrameworkInterceptionInterceptor;

    public function __construct()
    {
        $this->___init();
    }

    /**
     * {@inheritdoc}
     */
    public function addField(MagentoFrameworkDataCollection $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);
        }
    }
}

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

DI плагины в Magento 2 - 2
Оранжевым подсвечен класс, сгенерированный 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);

Ссылки

Автор: flancer

Источник

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


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js