How to use Plugin, Preference to rewrite Block, Model, Controller, Helper in Magento 2

When you use block, model, controller, helper in Magento 2, it is not good if you modify the core files, that may have certain influence on another program. Therefore, today, I highly recommend the great way to rewrite all files in a convenient way. This is the topic to show you how to rewrite block, model, controller, helper when using plugin and preference in Magento 2.

Overview of rewriting block, model, controller, helper

  • Method 1: Using Plugin
  • Method 2: Using Preference

Method 1: Using Plugin

Because of the big inconvenience, if using the preference, Plugin appears as the clever choice to rewrite block, model, controller, helper in Magento 2. With Plugin, you can execute the code before, after and around the code/target class’s function. Without replacing, this is just inserting some code before/after the core code and then observe the core/target class’s function and run our code in-between the core/target class’s function. In addition, it is possible to the plugins from multiple modules to insert their own code before/after/around the same core/target class’s function.

BLOCK OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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\Block\Product\View">
        <plugin name="Mageplaza-yourmodule-product-block" type="Mageplaza\HelloWorld\Plugin\ProductPlugin" sortOrder="5" />
    </type>
</config>

Here enable all the methods containing before, after, and around methods.

  • Firstly, beforeGetProduct method will be active.
  • Next, aroundGetPrduct will be active.

Note: Using around method means you can insert code directly both before and after the observed function. Specifically, the function I want to mention is getProduct() and that is observed in the class Magento\Catalog\Block\Product\View.

  • Finally, afterGetProduct method will be active. You can look into the var/log/debug.log and confirm the method execution sequence.

app/code/Mageplaza/HelloWorld/Plugin/ProductPlugin.php

<?php
 
namespace Mageplaza\HelloWorld\Plugin;
 
class ProductPlugin
{    
    public function beforeGetProduct(\Magento\Catalog\Block\Product\View $subject)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug(__METHOD__ . ' - ' . __LINE__);        
    }
    
    public function afterGetProduct(\Magento\Catalog\Block\Product\View $subject, $result)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        return $result;
    }
    
    public function aroundGetProduct(\Magento\Catalog\Block\Product\View $subject, \Closure $proceed)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        // call the core observed function
        $returnValue = $proceed(); 
        
        // logging to test override        
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        return $returnValue;
    }    
}
?>

MODEL OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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="Mageplaza-yourmodule-product-model" type="Mageplaza\HelloWorld\Plugin\ProductPlugin" sortOrder="1" />
    </type>    
</config>

In this command, “before” and “after” method are enabled to run the code before and after the observed method getName($product).

app/code/Mageplaza/HelloWorld/Plugin/ProductPlugin.php

<?php
 
namespace Mageplaza\HelloWorld\Plugin;
 
class ProductPlugin
{    
    public function beforeSetName(\Magento\Catalog\Model\Product $subject, $name)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Model Override Test before');
        
        return $name;
    }
            
    public function afterGetName(\Magento\Catalog\Model\Product $subject, $result)
    {            
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Model Override Test after');
        
        return $result;
    }    
}
?>

CONTROLLER OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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\Controller\Product\View">
        <plugin name="MageplazaHelloWorldControllerProductView" type="Mageplaza\HelloWorld\Plugin\ProductPlugin" sortOrder="10"/>
    </type>
</config>

In this command, “around” method are enabled to run the code before and after the observed method execute present in class Magento\Catalog\Controller\Product\View.

app/code/Mageplaza/HelloWorld/Plugin/ProductPlugin.php

<?php
 
namespace Mageplaza\HelloWorld\Plugin;
 
class ProductPlugin
{        
    public function aroundExecute(\Magento\Catalog\Controller\Product\View $subject, \Closure $proceed)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        // call the core observed function
        $returnValue = $proceed(); 
        
        // logging to test override        
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        return $returnValue;
    }
}
?>

HELPER OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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\Helper\Data">
        <plugin name="MageplazaHelloWorldHelperData" type="Mageplaza\HelloWorld\Plugin\ProductPlugin" sortOrder="10"/>
    </type>
</config>

In this command, “around” method are enabled to run the code before and after the observed method getProduct() present in class Magento\Catalog\Controller\Product\View.

app/code/Mageplaza/HelloWorld/Plugin/ProductPlugin.php

<?php
 
namespace Mageplaza\HelloWorld\Plugin;
 
class ProductPlugin
{    
    public function aroundGetProduct(\Magento\Catalog\Helper\Data $subject, \Closure $proceed)
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        // call the core observed function
        $returnValue = $proceed(); 
        
        // logging to test override        
        $logger->debug(__METHOD__ . ' - ' . __LINE__);
        
        return $returnValue;
    }
}
?>

Method 2: Using Preference

Preference is called as a class rewrite in Magento 1. Now you will know what you should do with the preference in order to rewrite block, model, controller, helper.

BLOCK OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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">
    <preference for="Magento\Catalog\Block\Product\View" type="Mageplaza\HelloWorld\Block\Catalog\Product\View" />
</config>

At that time, you are allowed to rewrite getProduct() function of class Magento\Catalog\Block\Product\View, then you only need to log some message on var/log/debug.log for this test.

app/code/Mageplaza/HelloWorld/Block/Catalog/Product/View.php

<?php
 
namespace Mageplaza\HelloWorld\Block\Catalog\Product;
 
class View extends \Magento\Catalog\Block\Product\View
{
    /**
     * Retrieve current product model
     *
     * @return \Magento\Catalog\Model\Product
     */
    public function getProduct()
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Block Override Test');
        
        if (!$this->_coreRegistry->registry('product') && $this->getProductId()) {
            $product = $this->productRepository->getById($this->getProductId());
            $this->_coreRegistry->register('product', $product);
        }
        return $this->_coreRegistry->registry('product');
    }
}
?>

MODEL OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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">    
    <preference for="Magento\Catalog\Model\Product" type="Mageplaza\HelloWorld\Model\Catalog\Product" />
</config>

At that time, you are allowed to rewrite getName() function of class Magento\Catalog\Model\Product, then you only need to log some message on var/log/debug.log for this test.

app/code/Mageplaza/HelloWorld/Model/Catalog/Product.php

<?php
 
namespace Mageplaza\HelloWorld\Model\Catalog;
 
class Product extends \Magento\Catalog\Model\Product
{    
    /**
     * Get product name
     *
     * @return string
     * @codeCoverageIgnoreStart
     */
    public function getName()
    {        
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Model Override Test');
    
        return $this->_getData(self::NAME);
    }
}
?>

CONTROLLER OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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">    
    <preference for="Magento\Catalog\Controller\Product\View" type="Mageplaza\HelloWorld\Controller\Catalog\Product\View" />
</config>

At that time, you are allowed to rewrite execute() function of class Magento\Catalog\Controller\Product\View, then you only need to log some message on var/log/debug.log for this test.

app/code/Mageplaza/HelloWorld/Controller/Product/View.php

<?php
 
namespace Mageplaza\HelloWorld\Controller\Catalog\Product;
 
class View extends \Magento\Catalog\Controller\Product\View
{    
    /**
     * Product view action
     *
     * @return \Magento\Framework\Controller\Result\Forward|\Magento\Framework\Controller\Result\Redirect
     */
    public function execute()
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Controller Override Test');
        
        // Get initial data from request
        $categoryId = (int) $this->getRequest()->getParam('category', false);
        $productId = (int) $this->getRequest()->getParam('id');
        $specifyOptions = $this->getRequest()->getParam('options');
 
        if ($this->getRequest()->isPost() && $this->getRequest()->getParam(self::PARAM_NAME_URL_ENCODED)) {
            $product = $this->_initProduct();
            if (!$product) {
                return $this->noProductRedirect();
            }
            if ($specifyOptions) {
                $notice = $product->getTypeInstance()->getSpecifyOptionMessage();
                $this->messageManager->addNotice($notice);
            }
            if ($this->getRequest()->isAjax()) {
                $this->getResponse()->representJson(
                    $this->_objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode([
                        'backUrl' => $this->_redirect->getRedirectUrl()
                    ])
                );
                return;
            }
            $resultRedirect = $this->resultRedirectFactory->create();
            $resultRedirect->setRefererOrBaseUrl();
            return $resultRedirect;
        }
 
        // Prepare helper and params
        $params = new \Magento\Framework\DataObject();
        $params->setCategoryId($categoryId);
        $params->setSpecifyOptions($specifyOptions);
 
        // Render page
        try {
            $page = $this->resultPageFactory->create(false, ['isIsolated' => true]);
            $this->viewHelper->prepareAndRender($page, $productId, $this, $params);
            return $page;
        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
            return $this->noProductRedirect();
        } catch (\Exception $e) {
            $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
            $resultForward = $this->resultForwardFactory->create();
            $resultForward->forward('noroute');
            return $resultForward;
        }
    }    
}
?>

HELPER OVERRIDE

app/code/Mageplaza/HelloWorld/etc/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">
    <preference for="Magento\Catalog\Helper\Data" type="Mageplaza\HelloWorld\Helper\Catalog\Data" />
</config>

At that time, you are allowed to rewrite getProduct() function of class Magento\Catalog\Helper\Data, then you only need to log some message on var/log/debug.log for this test.

app/code/Mageplaza/HelloWorld/Helper/Catalog/Data.php

<?php
 
namespace Mageplaza\HelloWorld\Helper\Catalog;
 
class Data extends \Magento\Catalog\Helper\Data
{    
    /**
     * Retrieve current Product object
     *
     * @return \Magento\Catalog\Model\Product|null
     */
    public function getProduct()
    {
        // logging to test override    
        $logger = \Magento\Framework\App\ObjectManager::getInstance()->get('\Psr\Log\LoggerInterface');
        $logger->debug('Helper Override Test');
        
        return $this->_coreRegistry->registry('current_product');
    }
}
?>

However, the preference may cause inconvenient conflicts if two or more modules try to rewrite the same core class.

If you still meet any trouble, please leave comment on this topic, we will assist you as soon as possible.



Next tutorial:

Module Development Series



Enjoyed the tutorial? Spread it to your friends!

magento-2-tutorial
use
plugin
preference
rewrite
block
model
controller
helper

Module Development

Comments for How to use Plugin, Preference to rewrite Block, Model, Controller, Helper in Magento 2

Please leave comments if you have any questions, feedbacks.

You also may like these Magento 2 Extensions

One Step Checkout

$199
10 reviews

Layered Navigation

$99
12 reviews

SEO

FREE
no review

Shop By Brand

$99
3 reviews

People also searched for:

  • magento 2 use plugin preference rewrite block model controller helper
  • magento 2 plugin example
  • magento 2 plugin not working
  • magento 2 preference
  • magento 2 plugin after example
  • magento 2 interceptor
  • magento 2 plugin for controller
  • magento 2 plugin tutorial
  • magento 2 plugin class doesn't exist
  • use extension in magento 2
  • how to use plugin in magento 2
  • how to use extension in magento 2
  • how to use module in magento 2
  • How to use Plugin in Magento 2
  • magento 2 use plugin