Routing in Magento is one of the most important parts. The entire application flow (Magento 2) depends on handling the URL request, and the router class is responsible for matching and processing the request. So what is routing in Magento 2 and how does it work? In this article, we will go into detail and analyze it.
1. Handling flow for a normal Magento 2 request
- First, we will analyze the complete Magento 2 routing flow. As you all know, Magento 2 creates an HTTP application in the request flow that the class (initialization method) will start. The flow begins to work with the creation of front controller :
$frontController=$this->_objectManager->get('Magento\Framework\App\ FrontControllerInterface ).
- The front controller is responsible for looping through all available routers and the router is responsible for matching current requirements. Now in order to understand the overall flow, it is important to know how the application matches the routers. The list of routers is created in RouterList (called in Front Controller to loop on routers) class, is in Magento\Framework\App, and this class is responsible for ordering and iterating on the list of routers. The router class is in charge of matching if the router is responsible for the current request. The following is Magento 2 flow:
index.php(runs bootstrap and create HTTP application) -> HTTP app -> FrontController -> Routing -> Controller processing -> etc
Now we will analyze each part of routing flow to understand Magento 2 routing.
2. Frontend area routers
etc->frontend->routes.xml
To register a frontend route, we must create a routes.xml file. For example:
file: app/code/Bss/HelloWorld/etc/frontend/routes.xml
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <!--Use router 'standard' for frontend route--> <router id="standard"> <!--Define a custom route with id and frontName--> <route frontName="helloworld" id="helloworld"> <!--The module which this route match to--> <module name="Bss_HelloWorld"/> </route> </router> </config>
Explanation:
<router id="standard">
“standard” is one of the names in table 1: Specification of the router names in Magento
route frontName="helloworld" id="helloworld">
The id attribute is the only string that will identify this route. You will use this string to declare the layout handle for this module’s actions. helleworld_controller_action.xml
for example
The frontName
attribute is also the only string that will show up on the URL request. For example: <store-url>/<store-code>/helloworld/controller/action
<module name="Bss_module">
specifies modules’ name
- Create action class in the folder:
{namespace}/{module}/Controller/{Controller}/{Action}.php
Table 1: Names used in <id = "Name">
router in routes.xml file
Name( %routerId% ) |
Sort order | Description |
robots | 10 | Match the request to the robots.txt file |
urlrewrite | 20 | Match the request to the URL specified in the database |
standard | 30 | Router standards |
cms | 60 | Matching requirements for CMS pages |
default | 100 | Router default |
3. Adminhtml area routers
This route will be the same as the frontend router, but must be declared in the adminhtml directory (etc ->adminhtml->router.xml
)
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <!--Use router 'admin' for admin route --> <router id="admin"> <!--Define a custom route with id and frontName --> <route id="helloworld" frontName="helloworld"> <!--The module which this route match to--> <module name="Bss_HelloWorld"/> </route> </router> </config>
Explanation:
<router id="admin">
where “admin” is one of the names in table 2: Specification of the router names in Magento
- The admin page URL is similarly structured to the frontend page, but the
admin_area
name will be appended beforeroute_frontName
to identify this as the admin router.
E.g. <store-url>/<store-code>/admin/helloworld/controller/action
- The controller action for the admin page will be in the
Controller/Adminhtml
directory for example:{namespace}/{module}/Controller/Adminhtml/{Controller}/{Action}.php
Table 2:
Name(%routerId% ) |
Sort order | Description |
admin | 10 | Match the request to the admin Magento area |
default | 100 | Default router for the admin zone |
4. Standard router
- Format URL:
<store-url>/<store-code>/<front-name>/<controller-name>/<action-name>
<store-url
> specifies the base URL for the Magento version<store-code>
specifies the store context<frontend-name>
specifies the frontName of the FrontController to be used (frontname inroutes.xml
)<controller-name>
specifies the name of the controller<action-name>
specifies the action class to execute in the controller class
=> The router standard parses this URL format and matches the controller with exact action.
5. Custom router
- Front Controller will go through all routers in
routerList
(created from configuration inrouters.xml
) so we need to add custom router inlib/internal/Magento/Framework/App/RouterList.php
by adding configuration in configuration for 1 router in thedi.xml
module. We will create a new module and a new router inroutersList
and finally create a router class. - We can create a custom router by adding a configuration to
di.xml
inetc/frontend
(if we only use custom route in frontend). Finally, we createRouter.php
in the Controller directory with the appropriate router logic. We’ll find the URL and check if there is a specific word in the URL and then depending on that word, we set up the module front name, controller path name, action name and then request a forward for the base controller. - To add your custom router to the router list for FrontController, add contents to the
di.xml
module.
<type name="Magento\Framework\App\RouterList"> <arguments> <argument name="routerList" xsi:type="array"> <item name="%name%" xsi:type="array"> <item name="class" xsi:type="string">%classpath%</item> <item name="disable" xsi:type="boolean">false</item> <item name="sortOrder" xsi:type="string">%sortorder%</item> </item> </argument> </arguments> </type>
Specifically:
% name%
: The unique name of your router in Magento% classpath%
: the path of the router class>
for example: Bss\Agencies\Controller\Router\Custom
% sortorder%
: The sort order of the custom route in the router list
Create CustomRouter class
namespace Vendor\Module\Controller;
<?php namespace Bss\Agencies\Controller\Router; use Magento\Framework\App\Action\Forward; use Magento\Framework\App\ActionFactory; use Magento\Framework\App\ActionInterface; use Magento\Framework\App\RequestInterface; use Magento\Framework\App\RouterInterface; /** * Class CustomRouter * * @package Bss\Agencies\Controller */ class CustomRouter implements RouterInterface { /** * @var ActionFactory */ protected $actionFactory; /** * CustomRouter constructor. * @param ActionFactory $actionFactory */ public function __construct( ActionFactory $actionFactory ) { $this->actionFactory = $actionFactory; } /** * Match Router * * @param RequestInterface $request * @return ActionInterface */ public function match(RequestInterface $request) { $identifier = trim($request->getPathInfo(), '/'); $paramsArr = explode('/', $identifier); if (count($paramsArr) == 1 && $paramsArr[0] == 'magento-development-company') { $request->setModuleName('agencies') //module name ->setControllerName('agencies') //controller name ->setActionName('index')//action name; } else { $request->setModuleName('agencies'); //module name $request->setControllerName('agencies') //controller name; $request->setParam("company", $paramsArr[1]) // truyền tham số ; $request->setActionName("company")//action name;; } return $this->actionFactory->create(Forward::class); }
=> when we want to create custom router: frontName/agencies/company
to frontName/magento-development-company/company/
or frontName/agencies/index
to frontName/magento-development-company
6. Before and after in routes.xml
Add a “before” or after parameter in the module entry to record or extend the existing routes modules
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="standard"> <route id="customer"> <module name="Bss_Routing" before="Magento_Customer" /> </route> </router> </config>
This configuration requires searching for actions in the Bss_Routing
module before searching in the Magento_Customer
module. For example, if there exists app/code/Bss/Routing/Controller/Account/Login
we will use this file for processing login route instead of the original class.
7. Result object
- Json: You will use json result when you want to return json object. You will do this if you implement a custom API endpoint or a simple AJAX endpoint
- Example: class returns data which is an array with information on name, age
<?php namespace Bss\Hello\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\Result\JsonFactory; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\View\Result\Page; /** * Class Json * * @package Bss\Hello\Controller\Router */ class Json extends Action\Action { protected $_resultJsonFactory; /** * RouterJson constructor. * @param Context $context * @param JsonFactory $resultJsonFactory */ public function __construct( Context $context, JsonFactory $resultJsonFactory ) { $this->_resultJsonFactory = $resultJsonFactory; parent::__construct($context); } /** * Return information * * @return ResponseInterface|ResultInterface|Page */ public function execute() { $result = $this->_resultJsonFactory->create(); $params = [ 'Name' => "BSS", "Age" => "7 years old", ]; return $result->setData($params); } }
Results:
- Raw: If you want to use raw results to return a plain string with no Magento layout and view rendering. By default raw results will return a
text/html
header. If you want something else (text/xml
, text plain) then you will usesetHeader
<?php namespace Bss\Hello\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\View\Result\Page; /** * Class Raw * * @package Bss\Hello\Controller\Router */ class Raw extends Action\Action { protected $rawResultFactory; /** * Raw constructor. * * @param Context $context * @param RawFactory $rawResultFactory */ public function __construct( Context $context, \Magento\Framework\Controller\Result\RawFactory $rawResultFactory ) { $this->rawResultFactory = $rawResultFactory; parent::__construct($context); } /** * Return result raw * * @return ResponseInterface|\Magento\Framework\Controller\Result\Json|ResultInterface */ public function execute() { $result = $this->rawResultFactory->create(); $result->setHeader('Content-Type', 'text/xml'); $result->setContents('<root><science></science></root>'); return $result; } }
Results:
- Redirect: When you want to send user/request to a new URL through redirecting HTTP location header ( URL will change)
- Example of how to redirect and send the array data to the home page at the same time
<?php namespace Bss\Hello\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Controller\ResultInterface; /** * Class BssRedirect * * @package Bss\Helloworld\Controller\Router */ class BssRedirect extends Action\Action { protected $_pageFactory; /** * BssRedirect constructor. * @param Context $context */ public function __construct( Context $context ) { $this->resultFactory = $context->getResultFactory(); return parent::__construct($context); } /** * Redirect home page * * @return ResponseInterface|Redirect|ResultInterface */ public function execute() { $data = [ 'name' => "Bss", 'age' => '7' ]; $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('cms/index/index', $data); return $resultRedirect; } }
When you access the URL hello/router/bssredirect
it automatically redirects to the URL cms/index/index/name/Bss/age/7/
where name/bss/age/7
is the parameter and the data parameter passed to the home page
Result:
- Forward: Internally call the execution method of another action class and do not trigger a new request from the browser, but the URL remains unchanged.
-> Forward the current page to the home page
<?php namespace Bss\Hellow\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\Result\Forward; use Magento\Framework\Controller\Result\ForwardFactory; use Magento\Framework\Controller\ResultInterface; /** * Class BssForward * * @package Bss\Internship\Controller\Internship */ class BssForward extends Action\Action { /** * @var ForwardFactory */ protected $_resultForwardFactory; /** * Page4 constructor. * @param Context $context * @param ForwardFactory $_resultForwardFactory */ public function __construct( Context $context, ForwardFactory $_resultForwardFactory ) { $this->_resultForwardFactory = $_resultForwardFactory; parent::__construct($context); } /** * Forward home page * * @return ResponseInterface|Forward|ResultInterface */ public function execute() { $resultForward = $this->_resultForwardFactory->create(); $resultForward->setController('index') ->setModule('cms') ->forward('index'); return $resultForward; } }
Results:
- Layout: You can use a generic response layout to render any type of layout
E.g.
<?php namespace Bss\Hello\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\View\Result\LayoutFactory; /** * Class RouterLayout * * @package Bss\Helloworld\Controller\Router */ class RouterLayout extends Action\Action { /** * @var */ protected $resultLayoutFactory; /** * RouterLayout constructor. * @param Context $context * @param LayoutFactory $rawFactory */ public function __construct(Context $context, LayoutFactory $rawFactory) { $this->resultLayoutFactory = $rawFactory; parent::__construct($context); } /** * @return ResponseInterface|ResultInterface */ public function execute() { // .. $result = $this->resultLayoutFactory->create(); return $result; } }
Results:
- Page: page triggers
layout.xml
to render into HTML
<?php namespace Bss\Helloworld\Controller\Router; use Magento\Framework\App\Action; use Magento\Framework\App\Action\Context; use Magento\Framework\App\ResponseInterface; use Magento\Framework\Controller\ResultInterface; use Magento\Framework\View\Result\Page; use Magento\Framework\View\Result\PageFactory; /** * Class RouterPage * * @package Bss\Helloworld\Controller\Router */ class RouterPage extends Action\Action { protected $_resultPageFactory; /** * RouterPage constructor. * @param Context $context * @param PageFactory $pageFactory */ public function __construct(Context $context, PageFactory $pageFactory) { $this->_resultPageFactory = $pageFactory; parent::__construct($context); } /** * Papare template * * @return ResponseInterface|ResultInterface|Page */ public function execute() { return $this->_resultPageFactory->create(); } }
Results: