cancel
Showing results for 
Search instead for 
Did you mean: 

Decomposition of Magento Controllers

lbajsarowiczpl
Core Maintainer

Magento 2.4 became a perfect opportunity to proceed with backwards-incompatible changes that were waiting for years. When I was speaking at Magento Conferences about replacing inheritance with composition, I was not aware that I was going to be a part of this history. One such change was this Pull Request and its followup introduced by Vinai Kopp. Althought this change was very expected, these PRs were not merged.

At the begining of 2020 Vinai encouraged me to continue the work on Controllers decomposition using his contribution. With his continuous support and tremendous work of Lena Orobei - together we finally delivered one of the biggest architectural changes towards decomposition of Controllers.

 

Solution

 

76703363-31e87900-66c9-11ea-85b1-1fb77453dda3.png

As a Module developer, to implement a new Controller Action you only have to implement the \Magento\Framework\App\ActionInterface.

The authentication mechanisms for Customers are also migrated!

 

Benefits

 

No need to extend

 

Module developers don't have to extend from any class to create a fully functional action controller.

 

Example controller GET Action

 

use Magento\Framework\App\Action\HttpGetActionInterface;
use Magento\Framework\View\Result\PageFactory;

class MyController implements HttpGetActionInterface
{
    /** @var PageFactory */
    protected $resultPageFactory;

    public function __construct(PageFactory $resultPageFactory)
    {
        $this->resultPageFactory = $resultPageFactory;
    }
    
    public function execute()
    {
        return $this->resultPageFactory->create();
    }
}

You may have noticed that we use \Magento\Framework\App\Action\HttpGetActionInterface. It is a method-specific Interface extending ActionInterface. If you want to explicitly define what methods are going to be handled by the Controller, the most common interfaces are:

  • \Magento\Framework\App\Action\HttpDeleteActionInterface
  • \Magento\Framework\App\Action\HttpGetActionInterface
  • \Magento\Framework\App\Action\HttpPostActionInterface
  • \Magento\Framework\App\Action\HttpPutActionInterface

Please be aware that the HEAD method is handled the same way that GET is.

 

Performance

 

Keeping in mind that previously \Magento\Framework\App\Action\Context was injected into Actions with a set of classes:

    /**
     * @param \Magento\Framework\App\RequestInterface $request
     * @param \Magento\Framework\App\ResponseInterface $response
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Framework\UrlInterface $url
     * @param \Magento\Framework\App\Response\RedirectInterface $redirect
     * @param \Magento\Framework\App\ActionFlag $actionFlag
     * @param \Magento\Framework\App\ViewInterface $view
     * @param \Magento\Framework\Message\ManagerInterface $messageManager
     * @param \Magento\Framework\Controller\Result\RedirectFactory $resultRedirectFactory
     * @param \Magento\Framework\Controller\ResultFactory $resultFactory
     */

There's no doubt that the new way of creating Controllers is much cleaner. The performance overhead caused by instantiating classes that are not used by the Controller is significally reduced.

 

Differences

 

  • Simple controllers like customer/account/logoutSuccess experience a 5% decrease in CPU time on generation. image

  • Complex controllers like customer/section/load experience a > 30% decrease in CPU time on generation. image

  • Common ones like catalog/category/view experience a 10% decrease in CPU time on generation. image

Performance measurements were performed using BlackFire.io, in an isolated Docker environment with cURL requests, not being affected by Browser/Network overhead. Thanks to Christophe Dujarric for BlackFire's support.

 

Testing

 

Controllers are easier to test (due to their reduced amount of dependencies).

Inheritance of AbstractAction forces you to use at least the same dependencies that the parent class has. Unit Test for Category View has more than 70 lines of mocking dependencies, mocking Context methods to return mocked dependencies.

With the Composition approach you can inject dependencies directly to your class and inject only the ones you need (for example, only the ones you are going to use with your Unit Tests).

 

Caution

 

  • Keep in mind that some Modules have their own AbstractAction. For example \Magento\Customer\Controller\AccountInterface additionally handles Customer Authentication.
  • Controller "Supertypes" are deprecated (\Magento\Backend\App\AbstractAction, \Magento\Framework\App\Action\Action, \Magento\Framework\App\Action\AbstractAction, Magento\Framework\App\Action\Action\AbstractAccount) and you should not use them anymore.
  • It is recommended to avoid code migration till 2.5.0 since 3-rd party observers may be subscribed to your controllers. Methods like getRequestgetResponsegetActionFlag are eliminated with the inheritance and it will lead to errors when accessing them through controller object from event.
  • It is recommended to use new approach for new code only starting 2.4.0 release.
  • Existing Magento controllers will not be migrated until 2.5.0 to keep backward compatibility.
6 Comments
keevitaja
Occasional Contributor

hi, will 2.5 provide backwards capability for

 

Magento\Framework\App\Action\Action

lemhannes
M1 Certified

Hi @lbajsarowiczpl 

sounds greate! I've just two questions regarding this change:

 

1. it would be greate to update the Magento documentation (https://devdocs.magento.com/videos/fundamentals/create-a-new-page/) I just noticed that the classes Action and AbstractAction has been deprecated but couldn't finde a proper recommandation how to handle this in "best pratice way".

 

2. it would be helpful to have a list of examples for typical use cases (forwarding, rendering pages, etc.)

 

Kind regards

Hannes

erfanimani
M2 Certified

Thank you @lbajsarowiczpl, this architectural change is much appreciated.

 

Is there a recommended pathway for migration of code such as plugins on the dispatch method of controller supertypes (AbstractAction::dispatch) and event observers (controller_action_postdispatch et al.)?

alainlandr02f6
New Contributor

Great this new feature

waqaralieg4ce4
Senior Member

Hello @lbajsarowiczpl 

 

Thanks for your efforts to improve admin controllers as well?

 

I was trying to do the same approach for admin controllers

 

As All the All Admin controllers are extending 

Magento\Backend\App\Action

and this controller is extending 

\Magento\Backend\App\AbstractAction

Which is Deprecated 


so is it Good Practice to decompose  Admin Controllers in Magento 2.4.3?

please Guide, Thank You

MarkShust
M2 Certified

@waqaralieg4ce4 unfortunately composition was never implemented into backend controllers (as of Magento 2.4.5), so inheritence is still the preferred (only) method to create controllers for the admin. They probably shouldn't have ever been tagged deprecated.