We continue to explore the latest version of the most popular ecommerce platform and today we are going to learn about Request routing in Magento 2, namely Front controller, Controllers, Routers and Responses. Technical extracts are included in the article, as usual, to make it more practical. Two more Magento 2 How-to’s are going to be published next week.
So let’s get down to work.
Front Controller
Routing in Magento 2 is based on Front Controller Pattern. Front Controller is a design pattern, in which one component is responsible for processing all the incoming inquiries, their redirection to the corresponding components, further processing of the results and returning the results back to the browser.
In Magento 2, this path starts from creating the front controller instance and subsequent calling \Magento\Framework\App\FrontControllerInterface::dispatch method in \Magento\Framework\App\Http::launch method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
$frontController = $this->_objectManager->get(\Magento\Framework\App\FrontControllerInterface::class); $result = $frontController->dispatch($this->_request); public function dispatch(RequestInterface $request) { \Magento\Framework\Profiler::start('routers_match'); $routingCycleCounter = 0; $result = null; while (!$request->isDispatched() && $routingCycleCounter++ < 100) { /** @var \Magento\Framework\App\RouterInterface $router */ foreach ($this->_routerList as $router) { try { $actionInstance = $router->match($request); if ($actionInstance) { $request->setDispatched(true); $this->response->setNoCacheHeaders(); if ($actionInstance instanceof \Magento\Framework\App\Action\AbstractAction) { $result = $actionInstance->dispatch($request); } else { $result = $actionInstance->execute(); } break; } } catch (\Magento\Framework\Exception\NotFoundException $e) { $request->initForward(); $request->setActionName('noroute'); $request->setDispatched(false); break; } } } \Magento\Framework\Profiler::stop('routers_match'); if ($routingCycleCounter > 100) { throw new \LogicException('Front controller reached 100 router match iterations'); } return $result; } |
If the required path is found successfully, FrontController iterates through the available routers call the method \Magento\Framework\App\Action\AbstractAction::dispatch of the corresponding controller.
Routers
All routers in Magento 2 should implement \Magento\Framework\App\RouterInterface interface and define \Magento\Framework\App\RouterInterface::match method. This method is responsible for matching and processing URL inquiries. In case of a successful match the router returns the corresponding controller instance. If the necessary controller is not found, Front Controller moves on to the next router.
Magento 2 comes with 4 routers:
- Base Router (\Magento\Framework\App\Router\Base) sets module front name, controller and action names, controller module and route name if found. Processes standard Magento URLs.
- CMS Router (\Magento\Cms\Controller\Router) is used for processing CMS pages. Sets module name to “cms”, controller name to “page”, action name to “view” and page id depending on the requested page. After that it forwards request but won’t dispatch it. This will lead to Front Controller starting the next cycle of router checking, on which Base Router based on the established path will call \Magento\Cms\Controller\Page\View, which will show the necessary page.
- UrlRewrite Router (\Magento\UrlRewrite\Controller\Router) is responsible for catalog URL rewrites. It uses Url Finder to find the necessary URL in the database, and after that returns the forward just like CMS Router.
- Default Router (\Magento\Framework\App\Router\DefaultRouter) is used when no other router is able to find the appropriate controller. It’s responsible for 404 Page.
Controllers
Controllers in Magento 2 differ from typical controllers in MVC applications. Magento 2 controllers are responsible for only one specific URL and contain only one execute method. This method is responsible for returning result object and occasional processing of input POST data. All controllers inherit \Magento\Framework\App\Action\Action class.
Partner With Us
Let's discuss how to grow your business. Get a Free Quote.The required controller is searched in the Base Router, and then it’s called in the Front Controller.
1 2 3 4 5 |
public function match(\Magento\Framework\App\RequestInterface $request) { $params = $this->parseRequest($request); return $this->matchAction($request, $params); } |
\Magento\Framework\App\Router\Base::parseRequest method is responsible for splitting the requested URL into segments, and \Magento\Framework\App\Router\Base::matchAction method tries to find a suitable controller based on the received segments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
protected function matchAction(\Magento\Framework\App\RequestInterface $request, array $params) { $moduleFrontName = $this->matchModuleFrontName($request, $params['moduleFrontName']); if (empty($moduleFrontName)) { return null; } /** * Searching router args by module name from route using it as key */ $modules = $this->_routeConfig->getModulesByFrontName($moduleFrontName); if (empty($modules) === true) { return null; } /** * Going through modules to find appropriate controller */ $currentModuleName = null; $actionPath = null; $action = null; $actionInstance = null; $actionPath = $this->matchActionPath($request, $params['actionPath']); $action = $request->getActionName() ?: ($params['actionName'] ?: $this->_defaultPath->getPart('action')); $this->_checkShouldBeSecure($request, '/' . $moduleFrontName . '/' . $actionPath . '/' . $action); foreach ($modules as $moduleName) { $currentModuleName = $moduleName; $actionClassName = $this->actionList->get($moduleName, $this->pathPrefix, $actionPath, $action); if (!$actionClassName || !is_subclass_of($actionClassName, $this->actionInterface)) { continue; } $actionInstance = $this->actionFactory->create($actionClassName); break; } if (null == $actionInstance) { $actionInstance = $this->getNotFoundAction($currentModuleName); if ($actionInstance === null) { return null; } $action = 'noroute'; } // set values only after all the checks are done $request->setModuleName($moduleFrontName); $request->setControllerName($actionPath); $request->setActionName($action); $request->setControllerModule($currentModuleName); $request->setRouteName($this->_routeConfig->getRouteByFrontName($moduleFrontName)); if (isset($params['variables'])) { $request->setParams($params['variables']); } return $actionInstance; } |
Each segment in URL contains information for finding the necessary controller. Segments in URL can be represented as follows: moduleFrontName/actionPath/actionName, where:
- moduleFrontName — modules front name as set in routes.xml file;
- actionPath — the path to the controller;
- actionName — the name of the controller.
For URL like custom-module/info/product the path to the controller class might look like this: app/code/Vendor/Custom_Module/Controllers/Info/ProductController.php.
Magento Development Services
Take your online store to the next level with BelVG Magento development
Click to visit the pageResponses
The controller in Magento 2 can return several response types depending on the purpose and the necessary result.
- Page Result (\Magento\Framework\View\Result\Page) is the most common type of response. By returning this object, the controller starts the standard page rendering based on the corresponding XML layout handle.
1 2 3 4 5 6 7 8 9 |
public function __construct( $pageFactory Magento\Framework\View\Result\PageFactory ) { $this->pageResultFactory = $pageFactory } public function execute() { return $this->pageResultFactory->create(); } |
- JSON Result (\Magento\Framework\Controller\Result\Json) allows you to return a response in JSON format. It can be used in API or AJAX requests.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public function __construct( Magento\Framework\Controller\Result\JsonFactory $jsonResultFactory, ) { $this->jsonResultFactory = $jsonResultFactory; } public function execute() { $result = $this->jsonResultFactory(); $class = new Class; $class->data = value; $result->setData($class); return $result; } |
- Raw Result (\Magento\Framework\Controller\Result\Raw) is used if you want to return a string to the browser without using Magento layout and view rendering.
1 2 3 4 5 6 7 8 9 10 11 12 |
public function __construct( Magento\Framework\Controller\Result\Raw $rawResultFactory , ) { $this->rawResultFactory = $rawResultFactory; } public function execute() { $result = $this->rawResultFactory->create(); $result->setHeader('Content-Type', 'text/xml'); $result->setContents('<root><block></block></root>); return $result; } |
- Forward Result (\Magento\Framework\Controller\Result\Forward) allows to call another method/controller without changing the URL or redirecting.
1 2 3 4 5 6 7 8 9 10 11 |
public function __construct( Magento\Framework\Controller\Result\ForwardFactory $resultForwardFactory ) { $this->resultForwardFactory = $resultForwardFactory; } public function execute() { $result = $this->resultForwardFactory->create(); $result->forward('noroute'); return $result; } |
- Redirect Result (\Magento\Framework\Controller\Result\Redirect) is used when a user needs to be redirected to a different URL.
1 2 3 4 5 6 7 8 9 10 11 |
public function __construct( Magento\Framework\Controller\Result\RedirectFactory $resultRedirectFactory ) { $this->resultRedirectFactory = $resultRedirectFactory; } public function execute() { $result = $this->resultRedirectFactory->create(); $result->setPath('*/*/index'); return $result; } |
We hope the article was helpful and led to better understanding of Request routing in Magento 2.
Need expert Magento 2 development for your online store? Turn to BelVG.
Magento Webdesign
Take your online store to the next level with BelVG Magento Webdesign
Visit the page
Thank you, Namarata! You are right, there was a mistake – we updated the article.
Nice Article Gennadiy Haritonov!!
However, I think Magento\Framework\Controller\Result\ForwardFactory should be used instead of Magento\Framework\Controller\Result\Forward\Factory
Also, Magento\Framework\Controller\Result\RedirectFactory should be used instead of Magento\Framework\Controller\Result\Redirect\Factory
Hi! Great thanks for your comment! You are right, and I will take your note into account in the future.
Very useful and helpful article.
I think the controller class path in your example (“For URL like custom-module/info/product the path to the controller class might look like this: app/code/Vendor/Custom_Module/Controllers/Info/ProductController.php) is wrong; in Magento 2 the correct path should be app/code/Vendor/Custom_Module/Controller/Info/Product.php
Hi, NileshT!
Thanks for appreciating my article, I hope you found it useful.
Nice Article…Now, with Magento 2.3 below routers are available for front-end area:
1. Robots
2. Urlrewrite
3. Standard
4. Cms
5. Default
Thanks for posting…