Magento 2 Routing

Routing is the most important part of any application or framework. This blogpost covers the request flow of magento or routing process in magento2. So lets start with our index.php which is an entry file for magento. So you can see the following code:-

$bootstrap->run($app);

Now moving on to run function of \Magento\Framework\App\Bootstrap see the below code:-

$response = $application->launch();
$response->sendResponse();

Here it calls the launch function of AppInterface which is impelmeented by Magento\Framework\App\Http, now lets look into the launch() function.

public function launch()
    {
        $areaCode = $this->_areaList->getCodeByFrontName($this->_request->getFrontName());
        $this->_state->setAreaCode($areaCode);
        $this->_objectManager->configure($this->_configLoader->load($areaCode));
        /** @var \Magento\Framework\App\FrontControllerInterface $frontController */
        $frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
        $result = $frontController->dispatch($this->_request);
        // TODO: Temporary solution until all controllers return ResultInterface (MAGETWO-28359)
        if ($result instanceof ResultInterface) {
            $this->registry->register('use_page_cache_plugin', true, true);
            $result->renderResult($this->_response);
        } elseif ($result instanceof HttpInterface) {
            $this->_response = $result;
        } else {
            throw new \InvalidArgumentException('Invalid return type');
        }
        // This event gives possibility to launch something before sending output (allow cookie setting)
        $eventParams = ['request' => $this->_request, 'response' => $this->_response];
        $this->_eventManager->dispatch('controller_front_send_response_before', $eventParams);
        return $this->_response;
    }

Our function is first fetching the area code i.e, frontend or admin, then it sets the area code and configure it. Then we are calling dispatch function of Magento\Framework\App\FrontControllerInterface

$frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
$result = $frontController->dispatch($this->_request);

Now lets look at dispatch method, similar to Magento1 it loops through all router.

/Magento/Framework/App/FrontController.php
class FrontController implements FrontControllerInterface
{
    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);
                        $actionInstance->getResponse()->setNoCacheHeaders();
                        $result = $actionInstance->dispatch($request);
                        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;
    }
}

$this->_routerList is coming from Magento\Framework\App\RouterList, if you will echo in getRouterInstance(), then you will get the list of all routers. (Note, try for page that does not exist so that all router name can be listed).
Below is the sequence of router and their class name respectively.

Standard - Magento\Framework\App\Router\Base
Urlrewrite - Magento\UrlRewrite\Controller\Router
Cms - Magento\Cms\Controller\Router
Default - Magento\Framework\App\Router\DefaultRouter

We will discuss these router in later part of post lets complete dispatch function first. Dispatch method loops through all router until any match is found or routingcyclecounter exceeds 100. Once any match is found request will be dispatched through that router and response will be shown accordingly.

Lets take a brief look on our routers responsibility now:-

Base or standard Router

This is located at Magento\Framework\App\Router\Base and is the first router in loop. Match() method will parse the request and will match the action using url(front name/action path/action/param 1/etc params/).

Url Rewrite Router

This is located in app/code/Magento/UrlRewrite/Controller/Router.php, if you look at match function then you can see the urlfinder which are fetching the entity-type and id from url key to generate required routing setting i.e, controller, action, and module. Once match is found these will be forwarded back to standard router for match.

Cms Router

It handles the cms request, it basically fetch the page id by using url-key and then set module name as cms, controller name as page and action as view i.e, app/code/Magento/Cms/Controller/Page/View.php controller. After setting these details rather than dispatching the request it break current loop and and start loop again to match it with base router.

Default Router

It is used when match is not found in any other router, used for 404 page error type request.

Leave a Comment.