4

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?

  • I think this is a great question! – camdixon Oct 30 '16 at 13:19
1

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');

  • "Once request path is defined as cms index index". You mean page_type id='cms_index_index', right? At the heart of my routing question: How does Magento figure that out? Your nice refs discuss routing involving Front controller looping through all routers to match their route patterns. Usually routes come from routes.xml files. But I can't find one with a route of '/'. I've looked in source like .../framework/App/Router/Base.php and .../module-cms/Controller/Router.php, but didn't see special handling for '/' or ''. So where is '/' or '' matched? – gwideman Oct 31 '16 at 1:19
  • @ gwideman Okay cool, Can you observe the Router.php file \vendor\magento\module-cms\Controller\Router.php – Krishna ijjada Oct 31 '16 at 6:53
  • Yes, that was my leading suspect for where home page detection might reside. But I'm not seeing where that might happen. – gwideman Oct 31 '16 at 6:56
  • OK, I finally tracked down where the empty route gets detected, and will write it up shortly. – gwideman Nov 2 '16 at 23:09
1

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/.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.