Плагины
Плагин — класс, который перехватывает вызов открытого метода другого класса. Он может изменить аргументы до вызова, результат после него или обернуть вызов целиком. Это основной способ поменять поведение ядра, не трогая его файлы.
Где лежит плагин
Классы плагинов кладут в папку Plugin модуля, а подключают в di.xml. Файлы создают вручную.
app/code/Acme/Hello/
├── Plugin/
│ └── ProductNamePlugin.php новый файл
└── etc/
├── module.xml
└── frontend/
├── di.xml новый файл
└── routes.xmlПлагин after
Допишем к названию каждого товара на витрине слово «Acme». Название возвращает метод getName() класса Magento\Catalog\Model\Product.
// app/code/Acme/Hello/Plugin/ProductNamePlugin.php
namespace Acme\Hello\Plugin;
use Magento\Catalog\Model\Product;
class ProductNamePlugin
{
public function afterGetName(Product $subject, $result)
{
return $result . ' (Acme)';
}
}Имя метода складывается из приставки after и имени перехватываемого метода с заглавной буквы. Первый параметр — объект, чей метод вызван, второй — результат исходного метода. То, что вернёт плагин, получит вызывающий код.
Регистрация
<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/frontend/di.xml -->
<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="acme_hello_product_name" type="Acme\Hello\Plugin\ProductNamePlugin" sortOrder="10"/>
</type>
</config>Выполните bin/magento cache:clean и откройте любую страницу товара: к названию добавится «(Acme)». Файл лежит в etc/frontend, поэтому в панели управления названия остаются прежними. Атрибут sortOrder задаёт порядок, если плагинов несколько.
Плагин before
Метод с приставкой before получает аргументы исходного метода и может их изменить:
// app/code/Acme/Hello/Plugin/ProductNamePlugin.php
public function beforeSetName(Product $subject, $name): array
{
return [trim((string) $name)];
}Перед каждым вызовом setName() из названия убираются пробелы по краям. Плагин возвращает массив новых аргументов или null, если менять их не нужно.
Плагин around
Метод around оборачивает исходный вызов целиком и сам решает, выполнять ли его:
// app/code/Acme/Hello/Plugin/ProductSavePlugin.php
namespace Acme\Hello\Plugin;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\LocalizedException;
class ProductSavePlugin
{
public function aroundSave(
ProductRepositoryInterface $subject,
callable $proceed,
ProductInterface $product,
$saveOptions = false,
) {
if ($product->getPrice() < 0) {
throw new LocalizedException(__('Цена не может быть отрицательной.'));
}
return $proceed($product, $saveOptions);
}
}Параметр $proceed — исходный метод вместе с остальными плагинами. Товар с отрицательной ценой не сохранится: вместо этого появится сообщение об ошибке. Плагин регистрируют так же, но для типа Magento\Catalog\Api\ProductRepositoryInterface и в общем etc/di.xml.
Какой вид выбрать
| Вид | Когда использовать |
|---|---|
before | Изменить аргументы |
after | Изменить результат |
around | Отменить вызов или выполнить код до и после него |
Плагин around — самый тяжёлый: он усложняет отладку и мешает другим плагинам. Берите его, только когда двух других недостаточно.
Ограничения
Плагин нельзя повесить на методы с видимостью private и protected, на статические методы, на конструктор и на классы и методы с ключевым словом final.
Частая ошибка
В плагине around забыли вызвать $proceed или вернуть его результат. Исходный метод и все следующие плагины молча перестают работать, а вызывающий код получает null. Если отменять вызов не нужно, плагин around всегда заканчивается строкой return $proceed(...) с теми же аргументами.