Question

A module usually contains a routes.xml file which, as I understand it, specifies the routes which the module handles.

I would like to know how routing is handled for the home page, for example for the sample data Luma theme home page. I haven't been able to find a corresponding route, for example in magento/modules-cms, which does list "CMS Home Page" (cms_index_index) in its page_types.xml as a page type that it handles.

Whatever the routing mechanism, I would like to know how it connects route '/' to cms_index_index.

(Related question: How does Luma theme home page work?)

Edit for focus:

I have read the official docs and several blog posts about how routing works in Magento 2 in general. In brief, by consulting all routers which get their match patterns ultimately from routes.xml files.

But the home page seems to be a special case. So the question boils down to:

Where is the special-case code or configuration that detects a route of '/', or possibly '' (if slashes get removed), and ultimately translates that to a page type of cms_index_index?

Was it helpful?

Solution

http://inchoo.net/magento-2/routing-in-magento-2/ on Magento 2 routing discusses the sequence of Routers: Base, CMS, UrlRewrite and finally Default Router.

FrontController::dispatch loops through these Routers, calling their match() method.

Normally route matching patterns are defined in routes.xml files associated with each module. However, the home page route is not found in any of those. Instead, it's handled as a special case, as follows.

The following process recognizes the home page empty path, and translates that to a data structure consisting of 3 elements: moduleFrontName, actionPath, actionName, which end up being 'cms', 'index', 'index', or 'cms_index_index' when composed as a page_type id.

magento/framework/App/Router/Base.php:

public function match(\Magento\Framework\App\RequestInterface $request)
{
    $params = $this->parseRequest($request);          // Detects empty path, puts 'cms' for moduleFrontName in $params
    return $this->matchAction($request, $params);     // Uses default 
}

protected function parseRequest(\Magento\Framework\App\RequestInterface $request)
{
    $output = [];
    $path = trim($request->getPathInfo(), '/');
    $params = explode('/', $path ? $path : $this->pathConfig->getDefaultPath());  // fills in 'cms' 
    ...
}

protected function matchAction(\Magento\Framework\App\RequestInterface $request, array $params)
{
    ...
    $actionPath = $this->matchActionPath($request, $params['actionPath']);   // calls defaultPath->getPart('actionPath'), fills in 'index'
    $action = $request->getActionName() ?: ($params['actionName'] ?: $this->_defaultPath->getPart('action'));   // fills in 'index'
    ...
}

magento/module-store/Model/PathConfig.php:

    public function getDefaultPath()
    {
        //  gets the value set for Stores > Configuration > [scope ] > Web > Default Pages > Default Web URL,
        //  which defaults to 'cms'.
        return $this->scopeConfig->getValue('web/default/front', ScopeInterface::SCOPE_STORE);
    }

As my added code comments note, the match($request) method calls parseRequest, in which an empty path causes a calls to scopeConfig->getValue for the value configured in admin for "Default Web URL", which is set to 'cms' in the default Magento 2 installation.

At that point in match(), only moduleFrontName has been determined. Next, match() calls matchAction($request, $params), which determines actionPath and actionName. for the home page case, each of these variables is determined from _defaultPath. And _defaultPath is dependency-injected when Router Base was originally instantiated from class DefaultPath. The class DefaultPath in turn gets that actual values for the parts of the path from dependency injection when constructed. I think this rabbit hole of dependency injection ends up at:

/app/etc/di.xml:

<preference for="Magento\Framework\App\DefaultPathInterface" type="Magento\Framework\App\DefaultPath\DefaultPath" />
...
<type name="Magento\Framework\App\DefaultPath\DefaultPath">
    <arguments>
        <argument name="parts" xsi:type="array">
            <item name="module" xsi:type="string">core</item>
            <item name="controller" xsi:type="string">index</item>
            <item name="action" xsi:type="string">index</item>
        </argument>
    </arguments>
</type>

(Note: the getDefaultPath() in parseRequest() fetches path-related admin settings, while the _defaultPath object gets developer-set defaults.)

Summarizing this last part: _defaultPath ultimately gets an object instantiated by a factory that constructs a DefaultPath instance with defaults from di.xml. For our home page example, 'module' is not used (because the code already calculated aka moduleFrontName = 'cms'), and the values for 'controller' and 'action' provide values for $actionPath and $action.

The code having determined value 'cms_index_index', when control gets handed over to the cms module, its subsequent behavior is partly determined by corresponding values from module-cms/etc/frontend/page_types.xml and /module-cms/view/frontend/layout/cms_index_index.xml. (Beyond the scope of the current question.)

For what it's worth, I found the key clue about getDefaultPath() in Alan Storm's blog post on routing in Magento One: http://alanstorm.com/magento_dispatch_standard_router/.

OTHER TIPS

You can observe the basic routing from below links.

Routing in Magento 2

Magento 2: Advanced Routing

How routing is handled for the home page ?

Once request path is defined as cms index index from routing then system dispatch/call module-cms/controller/index/index-> execute method.

if you observe below code

/vendor/magento/module-cms/Controller/Index/Index.php

public function execute($coreRoute = null)
{
    $pageId = $this->_objectManager->get(
        'Magento\Framework\App\Config\ScopeConfigInterface'
    )->getValue(
        \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE,
        \Magento\Store\Model\ScopeInterface::SCOPE_STORE
    );
    $resultPage = $this->_objectManager->get('Magento\Cms\Helper\Page')->prepareResultPage($this, $pageId);
    if (!$resultPage) {
        /** @var \Magento\Framework\Controller\Result\Forward $resultForward */
        $resultForward = $this->resultForwardFactory->create();
        $resultForward->forward('defaultIndex');
        return $resultForward;
    }
    return $resultPage;
}
  1. \Magento\Cms\Helper\Page::XML_PATH_HOME_PAGE returns default home page configured in admin

  2. Then next important function is Magento\Cms\Help\Page')->prepareResultPage($this, $pageId);

    /vendor/magento/module-cms/Helper/Page.php

  3. prepares the result page by adding the handle $resultPage->addHandle('cms_page_view');

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top