A-A+

Magento 2 路由映射

2015年10月17日 灵犀一指 Magento 2 路由映射已关闭评论 阅读 781 人 次
routing-image

路由是Magento最重要的一部分。完整的Magento 2应用流由依靠处理URL请求和负责匹配和处理这些请求的路由类组成。在这篇文章中我们就讨论下Magento 2 的路由流,并且拣几个默认安装后自带的路由做一下分析。顺便展示一下如何创建自定义路由。路由如何匹配动作 (Controller class), 更多地信息,将来会另开一片文章说道说道。

路由流

首先,我们要有一个完整的路由流得概念,然后再后面才能深入探讨细节。我们都知道,Magento2创建HTTP请求的方式就是那里请求就在那里创建。 我们的路由流随着前端控制器的创建而开始。

[php]$frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
[/php]

前端控制器负责对循环槽的所有可用的路由器和当前请求匹配责任路由器,稍后我们将介绍前端控制器的细节。目前,要了解完整的流程之前,最重要的是需要知道如何应用如何与路由器匹配的。路由器列表在RouterList类(称为前端控制器循环路由器),位于Magento\Framework\App,这个类是负责对路由器列表进行排序和迭代。路由器类负责匹配,如果该路由器负责当前请求。让我们来看看Magento2流:

index.php (runs bootstrap and create HTTP application) → HTTP app → FrontController → Routing → Controller processing → etc

为了更好的理解Magento 2的路由,我们挨个分析路由路径的每个部分。

前端控制器

与Magento 1相同,它是HTTP应用程序启动(启动方法)入口路由点。它负责匹配路由器的当前请求。位于lib/internal/Magento/Framework/App/FrontController.php。你可以看到FrontControllerInterface在HTTP中的应用。让我们来看看代码:

[php]

class Http implements \Magento\Framework\AppInterface
{
public function launch()
{
...
//Here Application is calling front controller and it's dispatch method
$frontController = $this->_objectManager->get('Magento\Framework\App\FrontControllerInterface');
$result = $frontController->dispatch($this->_request);
...
}
}

[/php]

现在,当我们知道如何和何时前端控制器被调用,我们再来看看前端控制器类和调度方法本身试怎么用的:

[php]
lib/internal/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;
}
}
[/php]

我们可以看到,调度方法在所有路由器(已启用的,我们稍后再路由器配置)中循环,直到一个找到匹配的路由器,并且请求被分派($request→setDispatched(true);)或路由循环计数器超过100。路由器可以匹配的,否则它会重复循环这个匹配过程。此外,路由器可以重定向和分派,同时它也可以被匹配和处理。路由器列表类在请求流中被编译。现在,我们继续看看路由器匹配(匹配方法)的工作原理,以及究竟是什么路由器。

路由

路由是负责匹配和处理URL请求的PHP类。通常来讲,Magento框架和核心都包含一些路由:例如 Base, DefaultRouter, Cms and UrlRewrite. 我们后面会挨个讲解他们的用途和工作原理. 路由用于路由接口交互RouterInterface. 我们先看看这个默认的路由:

Base Router → CMS Router → UrlRewrite Router → Default Router

(这就是路由循环 – FrontController::dispatch())

Base Router

位于lib/internal/Magento/Framework/App/Router/Base.php, 是循环中第一个路由,如果你是Magento1的开发者,你就明白他是起始路由。 匹配方法会解析请求和匹配行动,后者会设置模块前端名、控制器路径名、动作名、控制模块和路由名。 Base Router中MagentoURL (front name/action path/action/param 1/etc params/) 被匹配。

CMS Router

CMS Router 位于 app/code/Magento/Cms/Controller/Router.php,用于处理 CMS页面,将模块名字设置为 (module front name) 为“cms”, 控制器名字 (controller path name) 为“page” ,动作名字为 “view” – app/code/Magento/Cms/Controller/Page/View.php 控制器。设置好Base controller,会继续设置页面ID并转发,但不会分发。转发的意思是它会打破现有路由循环并重启当前循环 (it can do that 100 times max)。 该路由会匹配激活 View controller in Cms/Controller/Page 的Base Router,同时展示以保存的页面ID (found page ID depending on url).

UrlRewrite Router

Magnto 2  UrlRewrite有自己的路由, 你可能会想起Magento1中Url Rewrite就是Standard router的一部分,位于: app/code/Magento/UrlRewrite/Controller/Router.php ,他使用Url Finder来从数据库获取 匹配 url的 url rewrite:

[php]
$rewrite = $this->urlFinder->findOneByData(
[
UrlRewrite::ENTITY_TYPE => $oldRewrite->getEntityType(),
UrlRewrite::ENTITY_ID => $oldRewrite->getEntityId(),
UrlRewrite::STORE_ID => $this->storeManager->getStore()->getId(),
UrlRewrite::IS_AUTOGENERATED => 1,
]
);
[/php]

它会和CMS router一样执行转发动作。

Default Router

位于 lib/internal/Magento/Framework/App/Router/DefaultRouter.php存在于整个路由循环中。当其他Router都没有匹配的时候,就需要他出场了。Magento2中我们创建custom handle 来执行“Not found” 页面显示定义的内容。 这是一个DefaultRouter循环中没有route handler list的例子:

[php]
foreach ($this->noRouteHandlerList->getHandlers() as $noRouteHandler) {
if ($noRouteHandler->process($request)) {
break;
}
}
[/php]

Custom Router (with an example)

Front Controller 会通过routersList 中的所有路由(created from configuration in routes.xml), 所以我们需要在 lib/internal/Magento/Framework/App/RouterList.php 中定义新路由,方法为在di.xml 中增加新路由的配置文件。我们创建一个新模块 (假设名字为 Inchoo/CustomRouter),然后在routersList 增加新路由,最后创建路由类。

Custom router is just an example in which you can see how to match and forward request for Base router to match. First, we need to create folder structure for our module which is located in app/code/Inchoo/CustomRouter, and then we’ll create module.xml in etc folder and composer.json in module root with module informations. Now, we can create custom router by adding configuration to di.xml in etc/frontend folder because we want to have custom router only for frontend. Lastly, we’ll create Router.php in Controller folder with logic for matching router. We will search the URL and check if there is specific word in URL and then, depending on that word, we will set module front name, controller path name, action name and then forward request for base controller. We’ll search for two words: “examplerouter” and “exampletocms”. On “examplerouter” match, we will forward to Base router match format (by setting module front name to “inchootest”, controller path name to “test”, action name to “test”), and on “exampletocms”, we will forward to Base router to display About us page.

di.xml (located in etc/frontend)

[php]
<?xml version="1.0"?>
<!-- /** * Copyright © 2015 Inchoo d.o.o. * created by Zoran Salamun(zoran.salamun@inchoo.net) * Module is created for Custom Router demonstration */ -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Framework\App\RouterList">
<arguments>
<argument name="routerList" xsi:type="array">
<item name="inchoocustomrouter" xsi:type="array">
<item name="class" xsi:type="string">Inchoo\CustomRouter\Controller\Router</item>
<item name="disable" xsi:type="boolean">false</item>
<item name="sortOrder" xsi:type="string">22</item>
</item>
</argument>
</arguments>
</type>
</config>
[/php]

Router.php (located in Controller folder)

[php]
<?php
namespace Inchoo\CustomRouter\Controller;

/**
* Inchoo Custom router Controller Router
*
* @author Zoran Salamun <zoran.salamun@inchoo.net>
*/
class Router implements \Magento\Framework\App\RouterInterface
{
/**
* @var \Magento\Framework\App\ActionFactory
*/
protected $actionFactory;

/**
* Response
*
* @var \Magento\Framework\App\ResponseInterface
*/
protected $_response;

/**
* @param \Magento\Framework\App\ActionFactory $actionFactory
* @param \Magento\Framework\App\ResponseInterface $response
*/
public function __construct(
\Magento\Framework\App\ActionFactory $actionFactory,
\Magento\Framework\App\ResponseInterface $response
) {
$this->actionFactory = $actionFactory;
$this->_response = $response;
}

/**
* Validate and Match
*
* @param \Magento\Framework\App\RequestInterface $request
* @return bool
*/
public function match(\Magento\Framework\App\RequestInterface $request)
{
/*
* We will search “examplerouter” and “exampletocms” words and make forward depend on word
* -examplerouter will forward to base router to match inchootest front name, test controller path and test controller class
* -exampletocms will set front name to cms, controller path to page and action to view
*/
$identifier = trim($request->getPathInfo(), '/');
if(strpos($identifier, 'exampletocms') !== false) {
/*
* We must set module, controller path and action name + we will set page id 5 witch is about us page on
* default magento 2 installation with sample data.
*/
$request->setModuleName('cms')->setControllerName('page')->setActionName('view')->setParam('page_id', 5);
} else if(strpos($identifier, 'examplerouter') !== false) {
/*
* We must set module, controller path and action name for our controller class(Controller/Test/Test.php)
*/
$request->setModuleName('inchootest')->setControllerName('test')->setActionName('test');
} else {
//There is no match
return;
}

/*
* We have match and now we will forward action
*/
return $this->actionFactory->create(
'Magento\Framework\App\Action\Forward',
['request' => $request]
);
}
}
[/php]

routes.xml (located in etc/frontend)

[php]
<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
<router id="standard">
<route id="inchootest" frontName="inchootest">
<module name="Inchoo_CustomRouter" />
</route>
</router>
</config>
[/php]

Test.php (test controller action class)

[php]
<?php
/**
* Copyright © 2015 Inchoo d.o.o.
* created by Zoran Salamun(zoran.salamun@inchoo.net)
*/
namespace Inchoo\CustomRouter\Controller\Test;

class Test extends \Magento\Framework\App\Action\Action
{
/**
* Listing all images in gallery
* -@param gallery id
*/
public function execute()
{
die("Inchoo\\CustomRouter\\Controller\\Test\\Test controller execute()");
}
}
[/php]

You can see example module on:
https://github.com/zoransalamun/magento2-custom-router

Installation安装:

第一步配置指针:

composer config repositories.inchoocustomrouter vcs git@github.com:zoransalamun/magento2-custom-router.git

请求新的包:

composer require inchoo/custom-router:dev-master

启用 Inchoo CustomRouter模块

php bin/magento module:enable Inchoo_CustomRouter

刷新缓存:

php bin/magento setup:upgrade

现在,你对Magento 2 的Router有什么感觉呢?

欢迎在评论中分享你的想法。

标签:

评论已关闭!