Table of Contents
As defined by the official document,
“A plugin or interceptor is a class that modifies the behavior of public class functions by intercepting a function call and running code before, after, or around that function call and allows to substitute or extend the behavior of original, public methods for any class or interface.”
In the simple words,
“A plugin or interceptor is a way to insert code dynamically without changing the original class behavior. It allows extending the core functionality without any modification to the core files.”
Extensions that wish to intercept and change the behavior of a public method can create a Plugin
class.
This interception approach reduces conflicts among extensions that change the behavior of the same class or method. Your Plugin
class implementation changes the behavior of a class function, but it does not change the class itself. Magento calls these interceptors sequentially according to a configured sort order, so they do not conflict with one another.
Magento 1 allowed to customize different classes and methods by rewriting a class. Powerful, but in this way, no modules could rewrite the same class, and hence, no flexibility. To overcome the rewrite conflicts and instability, Magento 2 comes with inceptors or plugins! And, the post is everything about it from the reasons to use, its restrictions, types and the method to create plugin in Magento 2.
What are the Magento 2 plugins?
In Magento 2 you can create plugins (interceptors) that allow you to extend functionality and execute your own code before, after, or around any PHP class public method.
To prevent conflicts, plugins for the same class are accomplished in sequence based on their sort order. For a module developer, Magento 2 plugins bring some benefits:
- Forwarding any method call that is utilized on an object manager controlled object and taken programmatic action
- Modifying the return value of any method call that is utilized on an object manager controlled object
- Adjusting the arguments of any method call that is used on an object manager controlled object
- Proceeding likewise when other modules are in progress of the same method in the same or predictable way.
However, it comes with limitations
Magento 2 plugins limitation
For the same reason, plugins have certain limitations. Magento 2 Interception plugin doesn’t work with:
- Objects that are instantiated before
Magento\Framework\Interception
is bootstrapped - Final methods
- Final classes
- Any class that contains at least one final public method
- Non-public methods
- Class methods (such as static methods)
__construct
and__destruct
- Virtual types
Why should you use Interceptor or Plugins in Magento 2?
- Minimum confliction among extensions that change the behavior of the same class or method
- We can prevent collisions between plugins by using sort order attribute of plugin. Plugin can be called sequentially according to a sort order, so it does not conflict with other plugin class.
- No issue of rewriting the system
- Customize the same method in different modules
- Ability to modify the return value of any method call that is used on an object manager controlled object
- Ability to modify the arguments of any method call that is used on an object manager controlled object
- Plugin class does not change class itself but it only modifies the behavior of class function.
Declaring a plugin
The di.xml
file in your module declares a plugin for a class object:
<config>
<type name="{ObservedType}">
<plugin name="{pluginName}" type="{PluginClassName}" sortOrder="1" disabled="false" />
</type>
</config>
You must specify these elements:
type name
. A class or interface which the plugin observes.plugin name
. An arbitrary plugin name that identifies a plugin. Also used to merge the configurations for the plugin.plugin type
. The name of a plugin’s class or its virtual type. Use the following naming convention when you specify this element:\Vendor\Module\Plugin\<ClassName>
.
The following elements are optional:
plugin sortOrder
. Plugins that call the same method run them using this order.plugin disabled
. To disable a plugin, set this element totrue
. The default value isfalse
.
As the following example, we will edit app\code\Vendor\Module\etc\di.xml
, you need to insert the snippet:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Vendor\Module
\Controller\Index\Example"> <plugin name="Vendor_Module
_Plugin" type="Vendor\Module
\Plugin\ExamplePlugin" sortOrder="10" disabled="false" /> </type> </config>
For example, the following code define type name, we created ExamplePlugin.php
file at app/code/Vendor/Module/Controller/Index/
Contents would be:
<?php
namespace Vendor\Module
\Controller\Index; class ExamplePlugin extends \Magento\Framework\App\Action\Action { protected $title; public function execute() { echo $this->setTitle('Welcome'); echo $this->getTitle(); } public function setTitle($title) { return $this->title = $title; } public function getTitle() { return $this->title; } }
With plugin name, we created Example.php
file at app/code/Vendor/Module/Plugin/
Contents would be:
<?php
namespace Vendor\Module
\Plugin; class TestPlugin{ }
Defining a plugin
By applying code before, after, or around a public method, a plugin extends or modifies that method’s behavior.
The first argument for the before, after, and around methods is an object that provides access to all public methods of the observed method’s class.
Plugin method naming convention
It is a Magento best practice to capitalize the first letter of the class method name for which you want to create a plugin before adding before
, around
or after
prefixes to it.
For example, to create a plugin for the setName
method of some class:
... public function setName($name) { ... } ... |
In the plugin class, the setName
method may have one of the following names:
beforeSetName
aroundSetName
afterSetName
If the first letter in the name of the class method name for which you want to create a plugin is the underscore
character, then you do not need to capitalize it in the plugin class.
For example, to create a plugin for the _abc
method of some class:
...
public function _abc() {
...
}
...
Use the following method names for the _abc
method in the plugin class:
before_abc
around_abc
after_abc
Types of Plugins in Magento 2:
-
- Before listener
- After listener
- Around listener
1. Before Listener
Before listeners are used whenever we want to change the arguments of an original method or want to add some behavior before an original method is called.
Before methods are the first methods to run in an observed method, and these methods must have the same name to the observed one’s name while the prefix label is before
.
To apply the before methods for modifying the arguments of an observed method, you can return a modified argument. If there are multiple arguments, the returning will be carried out according to a range of those arguments. If the returning is invalid, that means the arguments for the observed method should not be modified.
- Before listeners are used whenever we want to change the arguments of an original method or want to add some behavior before an original method is called.
- First methods to run in an observed method
- Before listener is called by adding the prefix ‘before’ to the method name and setting the first letter of original method to capital.
- It lets you modify the parameters that will be used.
- Syntax: beforeMethodname()
Namespace Vendor\Module\Plugin; class Plugin { public function beforeMethodName(\Vendor\Module\Model\TargetClass $subject, $arg1, $arg2, $arg3 = null) { return [$arg1, $arg2, $arg3]; } }
Example:-
<?php
namespace Vendor\Module\Plugin;
class TestPlugin
{
public function beforeSetTitle(\Vendor\Module\Controller\Index\Example $subject, $title)
{
$title = $title . " to ";
echo __METHOD__ . "</br>";
return [$title];
}
}
Example 2: – When product is added, add qty 10 in the cart
We will use before listeners to change behavior of addProduct method of Magento\Checkout\Model\Cart.
This method is called whenever we add product to cart.
Original Method is public function addProduct($productInfo, $requestInfo = null)
Step 1: Create Configuration File
To use Plugins first of all we have to define it in 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\Checkout\Model\Cart"> <plugin name="MyCart" type="Thecoachsmb\QuantityChange\Plugin\Model\Cart" sortOrder="1" /> </type> </config>
Step 2: Write Function to change the quantity change logic
Now create file Cart.php in folder ‘Thecoachsmb\QuantityChange\Plugin\Model’.
Before listener is called by adding prefix ‘before’ to the method name and setting first letter of original method to capital.
Now method addProduct will become beforeAddProduct.
<?php namespace Thecoachsmb\QuantityChange\Plugin\Model; class Cart { public function beforeAddProduct( \Magento\Checkout\Model\Cart $subject, $productInfo, $requestInfo = null ) { $requestInfo['qty'] = 10; // increasing quantity to 10 return array($productInfo, $requestInfo); } }
Here we are changing parameters. We set quantity to 10. Now it will always add 10 quantities of the product whenever we add product to cart. So we will use before listener when we want to change parameter of an method.
2. After Listener
After methods start running right after the observed method is finished, and these methods must have the same name to the observed one’s name while the prefix label is “after”.
After methods have responsibility for editing the results of an observed method in the correct way and being asked to get a return value. Because Magento 2 after plugins also gain the access to input parameters. The ‘after’ prefix must be included in the name of this method.
- After listeners are used whenever we want to change the arguments of an original method or want to add some behavior after an original method is called.
- Starts running after the observed method is finished
- After listener is called by adding the prefix ‘after’ to the method name and setting the first letter of original method to capital.
- It let you modify the output
- Responsible for editing the results of an observed method in the right way and must have a return value.
- Syntax: afterMethodname()
For example:
Namespace Vendor\Module\Plugin; class Plugin { public function afterMethodName(\Vendor\Module\Model\TargetClass $subject, $result) { return $result; } }
Example:-
<?php
namespace Vendor\Module\Plugin;
class TestPlugin
{
public function afterSetTitle(\Vendor\Module\Controller\Index\Example $subject, $result)
{
echo __METHOD__ . "</br>";
return '<h1>'. $result . 'Thecoachsmb Site' .'</h1>';
}
}
Example 2: –
We will use after listeners to change behavior of getName method of Magento\Catalog\Model\Product.
This method returns the name of Product.
Original Method is
public function getName()
<?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="MyCart" type="Thecoachsmb\Check\Model\Product" sortOrder="1" /> </type> </config>
Now create file Product.php in folder ‘Thecoachsmb\Check\Model’.
After listener is called by adding prefix ‘after’ to the method name and setting first letter of original method to capital.
Now method addProduct will become afterGetName.
<?php namespace Thecoachsmb\Check\Model; class Product { public function afterGetName(\Magento\Catalog\Model\Product $subject, $result) { return "Apple ".$result; // Adding Apple in product name } }
3. Around Listener
Around listeners are used when we want to change both the arguments and the returned values of an original method or add some behavior before and after an original method is called.
Around methods enables the code to run before and after the observed method, so you can override a method. The ‘around’ prefix is included in the name of this method.
- Inceptors run before and after the observed method.
- Allows overriding a method
- Used when you want to change both the arguments and the returned values of the observed method or add behavior before and after the observed method is called.
- Around listener is called by adding the prefix ‘around’ to the method name and setting the first letter of original method to capital.
- It must have a return value
- Syntax: aroundMethodname()
Before the arrange of the original method’s argument, a callable
from around methods will be called to the next method in the chain, that means the next plugin or the observed function is also called.
The first input argument is $subject – the original class object, the second one is $proceed – the callable variable that allows executing original function and other plugins in a queue, next input parameters of the original function.
Note: In case the callable
is not declared, the calling to neither the next plugin nor the original method is achieved.
For example:
Namespace Vendor\Module\Plugin; class Plugin { public function aroundMethodName(\Vendor\Module\Model\TargetClass $subject, callable $proceed, $arg1, $arg2, $arg3) { $result = $proceed(); return $result; } }
Around methods enables to the execution of code both before and after the target method in one place.
$proceed argument is PHP closure, which in turn calls the target method.
Such methods help you to completely replace the target method.
Example:-
<?php
namespace Vendor\Module\Plugin;
class TestPlugin
{
public function aroundSetTitle(\Vendor\Module\Controller\Index\Example $subject, callable $proceed, $title)
{
echo __METHOD__ . " - Before proceed() </br>";
$result = $proceed($title);
echo __METHOD__ . " - After proceed() </br>";
return $result;
}
}
Example 2:-
We will use around listeners to change behavior of addProduct method of Magento\Checkout\Model\Cart.
Around listener is called by adding prefix ‘arround’ to the method name and setting first letter of original method to capital.
Now method addProduct will become aroundAddProduct.
<?php namespace Thecoachsmb\Check\Model; class Cart { public function aroundAddProduct( \Magento\Checkout\Model\Cart $subject, \Closure $proceed, $productInfo, $requestInfo = null ) { $requestInfo['qty'] = 10; // setting quantity to 10 $result = $proceed($productInfo, $requestInfo); // change result here return $result; } }
The around listener methods must have a return value.
The return value is formed in such way that the parameters following the $closure parameter in the around listener method definition are passed to the $closure function call in a sequential order.
How to disable Magento 2 plugins?
Magento 2 plugins can be disabled in a di.xml
file. To disable a plugin, set the disabled
parameter of the plugin declaration to true
.
<type name="Magento\Checkout\Block\Checkout\LayoutProcessor"> <plugin name="ProcessPaymentConfiguration" disabled="true"/> </type>
When ProcessPaymentConfiguration
is the name of the plugin declared in the vendor/magento/module-payment/etc/frontend/di.xml
.
Remember that the same class can be called two ways: with the leading slash or without.
\Magento\Checkout\Block\Checkout\LayoutProcessor
and Magento\Checkout\Block\Checkout\LayoutProcessor
are both valid.
When you want to disable the plugin, you need to use the same path format to call and disable the plugin.
Example:
For example there is a PHP class with public functions “setName”, “getName”, “save”:
<?php namespace VendorName\ModuleName\Folder; class SomeModel { private $name; public function setName($name, $storeId = 0) { $this->name[0] = $name; } public function getName($storeId = 0) { return $this->name[$storeId]; } public function save($fields, $customData) { /* some save logic here */ } }
Dependency Injections Compilation
After creating a new plugin, do not forget to regenerate the Dependency injection to make it work. Run this CLI command:
php bin/magento setup:di:compile