Magento 2 - Объяснение плагина и основной код magento
Я создавал свои собственные плагины в Magento 2, однако мне трудно понять ссылки на библиотеки ядра Magento. Допустим, у меня есть плагин, подобный приведенному ниже, почему мы знаем, что GetName
находится в этом каталоге \ Magento\Каталог\Модель\Продукт) и откуда мы знаем, что для этого требуется $product, $name?
Если бы я хотел изменить цену продукта, как бы я узнал, в какую библиотеку звонить? Угадываю ли я имя функции GetPrice
или есть способ, чтобы иметь возможность чтобы точно знать название функции, которую я хочу изменить, чтобы создать плагин?
<?php
namespace Inchoo\Custom\Plugins;
Class AfterProduct {
public function afterGetName(\Magento\Catalog\Model\Product $product,
$name){
$price = $product ->getData('price');
if ($price < 60 ) {
$name .= " -> cheap" ;
}
else {
$name .= " -> expensive";
}
return $name;
}
}
2 answers
Во-первых, взгляните на этот метод Magento\Framework\Interception\Interceptor::___callPlugins
. Именно здесь происходит волшебство. (Я объясню, почему вам нужно взглянуть на это позже).
Обратите внимание, что Magento\Framework\Interception\Interceptor
является признаком , а не классом. Таким образом, $this
внутри признака ссылается на объект, обладающий этим признаком.
Этот метод принимает 3 параметра.
- подключаемый метод,
- аргументы исходного метода
- некоторая информация о плагине.
Этот метод определяет переменную (то есть функцию), которая делает это.
if (isset($currentPluginInfo[DefinitionInterface::LISTENER_BEFORE])) {
Проверяет, есть ли у метода before
плагины.
если это так, он проходит через все объявленные плагины. (foreach ($currentPluginInfo[DefinitionInterface::LISTENER_BEFORE] as $code) {
)
.
и вызывает beforeOriginalMethod
(сам плагин), который получает в качестве параметра подключаемый класс ($this) и аргументы исходного метода
Затем он проверяет наличие around
плагинов (if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AROUND])) {
), аналогичных плагинам before
.
Разница в том, что метод плагина получает дополнительный параметр $next
, который а callable
переменная (функция), определенная выше. это используется для вызова исходного метода внутри плагина around
, поскольку плагин around
заменяет исходный метод.
Затем он проверяет наличие after
плагинов (if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AFTER])) {
)
это очень похоже на плагины before
. Разница в том, что при этом в качестве параметров также принимается результат исходного метода.
Что должны возвращать плагины.
-
before
Этот плагин полезен, если вы хотите измените аргументы исходного метода. Поэтому он должен возвращать массив с новыми значениями аргументов. Например, если вы подключаете методsetName
из модели продукта, он получает в качестве параметра строку с именем$name
. Ваш плагин должен возвращать массив со строкой[$name]
. Этот тип плагина может возвращать значение null. Это означает, что исходный метод получит параметры без изменений. -
around
должен возвращать то же самое, что и исходный метод. есть тоже подвох. Если вы не вызовете внутри своего плагина исходный метод (второй параметр, описанный выше), все остальные плагины, которые появятся после вашего, будут проигнорированы. - после. Должен возвращать то же самое, что и исходный метод.
Как происходит волшебство.
При создании экземпляра класса через ObjectManager magento проверяет, есть ли у этого класса методы pluginizable
(скажем, в 3 раза быстрее: D).
Смотрите здесь, какие методы/классы поддерживают плагины.
Короче говоря, он читает файлы di.xml
, проверяет наличие объявленных плагинов в классе, который он пытается создать, проверяет, есть ли у классов, объявленных как плагины, методы, которые начинаются с before
, after
, around
и это соответствует именам общедоступных методов класса, который он пытается создать.
Если он что-то находит, он создает класс, который расширяет исходный класс, и он называется Original\ClassName\Interceptor
(добавляет interceptor
в конце.) и создает экземпляр этого класса вместо исходного один.
Просто найдите в папке generated
файл с именем Interceptor.php
.
Все эти классы выглядят одинаково.
У них есть черта \Magento\Framework\Interception\Interceptor;
, которую я описал выше.
Конструктор такой же, как и исходный конструктор класса с дополнительным вызовом ___init()
, который находится в черте \Magento\Framework\Interception\Interceptor
.
Затем перечислены все подключаемые методы, и все они выглядят одинаково
public function someMethodHere(arguments here)
{
$pluginInfo = $this->pluginList->getNext($this->subjectType, 'someMethodHere');
if (!$pluginInfo) {
return parent::someMethodHere(arguments here);
} else {
return $this->___callPlugins('someMethodHere', func_get_args(), $pluginInfo);
}
}
Вот как создается ссылка из плагинов на реальные классы. Через дочерний класс исходного класса, который либо вызывает исходный метод класса, либо плагины, объявленные для этого класса.
Возможно, вы добавили di.xml файл.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Model\Product">
<plugin name="plugin_update_product_name" type="Inchoo\Custom\Plugins\AfterProduct"></plugin>
</type>
</config>
В приведенном выше di.xml файл, имя которого мы объявили="Magento\Каталог\Модель\Продукт", относится к классу, метод которого мы собираемся переопределить.
Таким образом, вы можете найти все связанные методы, которые вы можете использовать, перейдя в magento_rootdirectory/vendor/module-catalog/Model/Product.php файл.
Надеюсь, это поможет.