Several Plugins on One Method
sortOrder decides which plugin runs first, disabled="true" switches off an inherited one, and dev:di:info shows what is attached.
bin/magento dev:di:info 'Vendor\Module\Block\Thing' frontendMore than one module can plug into the same method. The order comes from sortOrder on each plugin node, and lower numbers run first. When two plugins share a number, or neither sets one, the order falls back to module load order (the sequence list in module.xml), which is not something to rely on.
The order
Magento walks the chain from the lowest sortOrder to the highest. On the way in it calls each before method and the first half of each around. The observed method runs. On the way out it calls each after method, again from lowest to highest. Each after receives the result the previous one returned, so the last plugin in the chain decides what the caller sees.
Turning one off
A plugin you inherit from a core or third-party module is switched off by repeating its name with disabled="true". You need the exact name used by the module that declared it; the type attribute can be left out. Write the class name the same way in both places, consistently with or without a leading backslash. Then run bin/magento cache:clean config.
Finding what is attached
bin/magento dev:di:info takes a class name and an optional area code. It prints the preference, the constructor parameters and a Plugins table of plugin, method and type. It is the quickest answer to the question of which plugin changed a value.
Both empty-element forms mean the same: <plugin name="x" disabled="true"/> and the same tag written out with a closing </plugin>.
Example
<!-- app/code/Vendor/Checkout/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\Checkout\Block\Checkout\LayoutProcessor">
<!-- runs first on the way in and first on the way out -->
<plugin name="vendor_checkout_relabel_company"
type="Vendor\Checkout\Plugin\RelabelCompanyField"
sortOrder="10"/>
<plugin name="vendor_checkout_log_layout"
type="Vendor\Checkout\Plugin\LogLayout"
sortOrder="20"/>
<!-- switch off a plugin another module declared -->
<plugin name="ProcessPaymentConfiguration" disabled="true"/>
</type>
</config>
<!-- Apply with: bin/magento cache:clean config -->When to use it
- Make your plugin run after a third-party plugin that rewrites the same value.
- Switch off a core plugin that adds a checkout field your build does not use.
- Find out which of eleven installed modules changed a price on a product page.
More examples
Asking Magento what is attached
The constructor table is abridged here. Two things to note: the area code is a second argument, and a disabled plugin does not appear in the table at all, which is how you confirm a disabled="true" took effect.
bin/magento dev:di:info 'Magento\Checkout\Block\Checkout\LayoutProcessor' frontend
DI configuration for the class Magento\Checkout\Block\Checkout\LayoutProcessor in the frontend area
Constructor Parameters:
+--------+-------------------------------------------------+------------------+
| Name | Requested Type | Configured Value |
+--------+-------------------------------------------------+------------------+
| merger | Magento\Checkout\Block\Checkout\AttributeMerger | |
+--------+-------------------------------------------------+------------------+
Plugins:
+-------------------------------------------+---------+-------+
| Plugin | Method | Type |
+-------------------------------------------+---------+-------+
| Vendor\Checkout\Plugin\RelabelCompanyField | process | after |
| Vendor\Checkout\Plugin\LogLayout | process | after |
+-------------------------------------------+---------+-------+A debug plugin that runs last
Give this a high sortOrder and it sees the value every other plugin has already changed, which tells you whether the damage happened before or after your own code. The line lands in var/log/debug.log in developer mode.
<?php
// app/code/Vendor/Checkout/Plugin/LogLayout.php
declare(strict_types=1);
namespace Vendor\Checkout\Plugin;
use Magento\Checkout\Block\Checkout\LayoutProcessor;
use Psr\Log\LoggerInterface;
class LogLayout
{
public function __construct(private readonly LoggerInterface $logger)
{
}
public function afterProcess(LayoutProcessor $subject, array $result): array
{
$this->logger->debug(
'checkout layout after plugins',
['top' => array_keys($result['components'] ?? [])]
);
return $result;
}
}Disabling a global plugin in one area only
Plugins declared in etc/di.xml apply in every area, and an area file can extend or override that. Here the plugin keeps running on the storefront and stops in the admin. Run bin/magento cache:clean config afterwards.
<!-- app/code/Vendor/Checkout/etc/adminhtml/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\Message\ManagerInterface">
<plugin name="vendor_greeting_prefix_success_message" disabled="true"/>
</type>
</config>
Discussion