19

how can i rewrite a controller (that's actually an action) in Magento 2?
I tried, as instructed here like this:

I have my own module called Namespace_Module with a di.xml file, that is taken into consideration, because the same system works on models and blocks
Example:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <!-- this one doesn't work for a controller action -->
    <preference for="Magento\Backend\Controller\Adminhtml\Dashboard\RefreshStatistics" 
         type="Namespace\Module\Controller\Adminhtml\Dashboard\RefreshStatistics" />
    <!-- this one works for a model -->
    <preference for="Magento\Customer\Model\Resource\GroupRepository" 
        type="Namespace\Module\Model\Resource\Customer\GroupRepository" />
     <!-- this one works also for a block -->
    <preference for="Magento\Backend\Block\Dashboard" 
        type="Namespace\Module\Block\Backend\Dashboard" />
</config>

I'm trying to replace the dashboard refresh statistics with my own action. Doing it like above, the execute method in the original class still gets called and not my own.
var/cache and var/generation were cleared.

2

3 Answers 3

18

Found it.
Actually what I posted in the question is the correct way of rewriting a controller.

<preference for="Magento\Backend\Controller\Adminhtml\Dashboard\RefreshStatistics" 
     type="Namespace\Module\Controller\Adminhtml\Dashboard\RefreshStatistics" />

works nicely.
The problem for me was this. I forgot to mention that I removed some modules Magento2 and among these it was the Reports module. I didn't state it in the question because I didn't think it was significant.
The method above for rewriting controllers (and maybe other classes) works if all the classes you are trying to change exist and all their parent classes also.
So the original Magento\Backend\Controller\Adminhtml\Dashboard\RefreshStatistics extends Magento\Reports\Controller\Adminhtml\Report\Statistics that I had removed.
In magento 2 the routes are collected by scanning the folders Controller folders for all the enabled modules and they are collected in an array.
So far so good.
I end up with this line among others:

[magento\backend\controller\adminhtml\dashboard\refreshstatistics] => Magento\Backend\Controller\Adminhtml\Dashboard\RefreshStatistics

Then the request is matched to the route magento\backend\controller\adminhtml\dashboard\refreshstatistics and Magento checks if the class corresponding to that route is subclass of Magento\Framework\App\ActionInterface. Since the routes are collected before my class should get identified and instantiated, the old class is validated instead of my own. And the parent class of the class Magento\Backend\Controller\Adminhtml\Dashboard\RefreshStatistics does not exist.

A solution to keep the reports module disabled but still make it work is to create an interceptor for the method that reads all the routes and replace the route mentioned above.

So I added this in di.xml

<type name="Magento\Framework\App\Router\ActionList\Reader">
    <plugin name="namespace-module-route" type="Namespace\Module\Model\Plugin\ActionListReader" sortOrder="100" />
</type>

and my plugin looks like this:

<?php
namespace Namespace\Module\Model\Plugin;

class ActionListReader
{
    public function afterRead(\Magento\Framework\App\Router\ActionList\Reader\Interceptor $subject, $actions)
    {
        $actions['magento\backend\controller\adminhtml\dashboard\refreshstatistics'] = 'Namespace\Module\Controller\Adminhtml\Dashboard\RefreshStatistics';
        return $actions;
    }
}
1
  • :- how to extend vendor\magento\module-directory\Model\PriceCurrency.php convertAndRound() , here i need to change precision , in this case how to use plugin it force me to use preference in this case Commented Feb 15, 2016 at 12:08
6

dont use preference use plugin to extend any core module that is in di.xml

<type name="Magento\Catalog\Controller\Product\View">
    <plugin name="product-cont-test-module" type="Sugarcode\Test\Model\Plugin\Product" sortOrder="10"/>
</type>

and in Product.php

public function aroundExecute(\Magento\Catalog\Controller\Product\View $subject, \Closure $proceed)
{
    echo 'I Am in Local Controller Before <br>';
    $returnValue = $proceed(); // it get you old function return value
    //$name='#'.$returnValue->getName().'#';
    //$returnValue->setName($name);
    echo 'I Am in Local Controller  After <br>';
    return $returnValue;// if its object make sure it return same object which you addition data
}

How to Override Core Block, Model and controller in Magento2

2
  • 2
    yes, this is the best practice. But in my case I removed the module that contained a class extended by the controller I'm trying to override. So around would not work for me. I wanted to completely change the behavior of the original controller.
    – Marius
    Commented Nov 23, 2015 at 8:01
  • if you want to change complete behavior then create one more new action and then just change the url where ever u needed , i hope this will be good idea Commented Nov 24, 2015 at 4:00
2

I have rewrite controller for review model. composer.json file:

{
        "name": "apple/module-review",
        "description": "N/A",
        "require": {
            "php": "~5.5.0|~5.6.0|~7.0.0",
            "magento/framework": "100.0.*"
        },
        "type": "magento2-module",
        "version": "100.0.2",
        "license": [
            "OSL-3.0",
            "AFL-3.0"
        ],
        "autoload": {
            "files": [
                "registration.php"
            ],
            "psr-4": {
                "Apple\\Review\\": ""
            }
        }
    }

registration.php file

    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Apple_Review',
        __DIR__
    );

app/code/Apple/Review/etc/module.xml file:

    app/code/Apple/Review/etc/di.xml file for override review controller.
    <?xml version="1.0"?>
    <!--
    /**
     * Copyright © 2015 Magento. All rights reserved.
     * See COPYING.txt for license details.
     */
    -->
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <preference for="Magento\Review\Controller\Product\Post" type="Apple\Review\Controller\Post" />   
    </config>

In controller file for review model,

app/code/Apple/Review/Controller/Post.php

    use Magento\Review\Controller\Product as ProductController;
    use Magento\Framework\Controller\ResultFactory;
    use Magento\Review\Model\Review;

    class Post extends \Magento\Review\Controller\Product\Post
    {
        public function execute()
        {
           $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
            if (!$this->formKeyValidator->validate($this->getRequest())) {
                $resultRedirect->setUrl($this->_redirect->getRefererUrl());
                return $resultRedirect;
            }

            $data = $this->reviewSession->getFormData(true);
            if ($data) {
                $rating = [];
                if (isset($data['ratings']) && is_array($data['ratings'])) {
                    $rating = $data['ratings'];
                }
            } else {
                $data = $this->getRequest()->getPostValue();
                $rating = $this->getRequest()->getParam('ratings', []);
            }

            if (($product = $this->initProduct()) && !empty($data)) {
                /** @var \Magento\Review\Model\Review $review */
                $review = $this->reviewFactory->create()->setData($data);

                $validate = $review->validate();
                if ($validate === true) {
                    try {
                        $review->setEntityId($review->getEntityIdByCode(Review::ENTITY_PRODUCT_CODE))
                            ->setEntityPkValue($product->getId())
                            ->setStatusId(Review::STATUS_PENDING)
                            ->setCustomerId($this->customerSession->getCustomerId())
                            ->setStoreId($this->storeManager->getStore()->getId())
                            ->setStores([$this->storeManager->getStore()->getId()])
                            ->save();

                        foreach ($rating as $ratingId => $optionId) {
                            $this->ratingFactory->create()
                                ->setRatingId($ratingId)
                                ->setReviewId($review->getId())
                                ->setCustomerId($this->customerSession->getCustomerId())
                                ->addOptionVote($optionId, $product->getId());
                        }

                        $review->aggregate();
                        $this->messageManager->addSuccess(__('You submitted your review for moderation.Thanks'));
                    } catch (\Exception $e) {
                        $this->reviewSession->setFormData($data);
                        $this->messageManager->addError(__('We can\'t post your review right now.'));
                    }
                } else {
                    $this->reviewSession->setFormData($data);
                    if (is_array($validate)) {
                        foreach ($validate as $errorMessage) {
                            $this->messageManager->addError($errorMessage);
                        }
                    } else {
                        $this->messageManager->addError(__('We can\'t post your review right now.'));
                    }
                }
            }
            $redirectUrl = $this->reviewSession->getRedirectUrl(true);
            $resultRedirect->setUrl($redirectUrl ?: $this->_redirect->getRedirectUrl());
            return $resultRedirect;
        }
    }

This is working code for review controller override in magento2. Thanks.

4
  • :- using preference is not good way to extend, use plugin concept Commented Nov 23, 2015 at 4:12
  • @PradeepKumar can you explain why using plugins is preferable to using preference?
    – scrowler
    Commented Dec 15, 2015 at 23:23
  • @robbie :- it keep original or core functionality, ex if magneto2 upgraded and some changes happen in same function then we will lose that part, so go for plugin it retain core logi Commented Dec 16, 2015 at 4:34
  • Plugin is mutually exclusive whereas preference is a rewrite- is that correct @PradeepKumar?
    – scrowler
    Commented Dec 16, 2015 at 4:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.