I've been creating my own plugins in Magento 2 however I'm struggling to understand references to the Magento core libraries. Say I have a plugin like the one below, why do we know that GetName
is in this directory \Magento\Catalog\Model\Product) and how do we know it requires $product, $name?
If I wanted to change the price of a product, how would I know which library to call? Do I guess at the function name GetPrice
or is there a way to be able to know for sure the name of the function I wish to change in order to create a plugin?
<?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;
}
}
Best Answer
First, take a look at this method
Magento\Framework\Interception\Interceptor::___callPlugins
. this is where the magic happens. (I'll explain why you need to look at this later).Notice that
Magento\Framework\Interception\Interceptor
is a trait and not a class. So$this
inside the trait references the object that has this trait.This method accepts 3 parameters.
This method defines a variable (that is a function) that does this.
checks if the method has
before
plugins. if it does, it loops through all declared plugins. (foreach ($currentPluginInfo[DefinitionInterface::LISTENER_BEFORE] as $code) {
) . and calls thebeforeOriginalMethod
(the plugin itself) that receives as parameter the class being pluginized ($this) and the arguments of the original methodThen it checks for
around
plugins (if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AROUND])) {
) similar as forbefore
plugins.The difference is that the plugin method receives an extra parameter
$next
which is acallable
the variable (function) defined above. this is used to be able to call the orignial method inside thearound
plugin since thearound
plugin replaces the original method.Then it checks for
after
plugins (if (isset($currentPluginInfo[DefinitionInterface::LISTENER_AFTER])) {
) this is very similar to thebefore
plugins. The difference is that this receives as parameters the result of the original method also.What the plugins should return.
before
This plugin is useful if you want to change the arguments of the original method. So it must return an array with the new values of the arguments. For example if you pluginize the methodsetName
from the product model, this one receives as parameter a string called$name
. Your plugin should return an array with a string[$name]
. This type of plugin can return null. This means that the original method will receive the parameters unchanged.around
should return the same thing as the original method returns. THere is also a catch. If you don't call inside your plugin the original method (second parameter described above) all other plugins that come after yours will be ignored.How the magic happens.
When instantiating a class through the ObjectManager magento checks if that class has methods that are
pluginizable
(say that 3 times fast :D). See here what methods / classes support plugins.long story short, it reads the
di.xml
files, checks for declared plugins to the class it tries to instantiate, checks if the classes that are declared as plugins have methods that start withbefore
,after
,around
and that match the public method names of the class it tries to instantiate.If it finds something it generates a class that extends the original class and it is called
Original\ClassName\Interceptor
(addsinterceptor
at the end.) and instantiates this class instead of the original one.Just look in the
generated
folder for a file namedInterceptor.php
.All these classes look the same.
They have the trait
\Magento\Framework\Interception\Interceptor;
, the one I described above.The constructor is the same as the original class constructor with an extra
___init()
call, which is located in the\Magento\Framework\Interception\Interceptor
trait.Then all the pluginized methods are listed and they all look the same
This is how the link is made from the plugins to the actual classes. Through a child class of the original class that either calls the original class method or the plugins declared for that class.