
この記事では、Magento 2 の Routing の重要な部分について説明します。ルートはモジュールの名前を定義するもので、モジュールを検索してコントローラのアクションを実行するためのURLに使用することができます。
1. Magento 2のリクエストフロー
Magento 2では、reques urlは以下のようになります。
http://example.com/index.php/front_name/controller/action
このURLの中には、モジュールを見つけるために使用される「front_name」があります。ルーターはこの名前を routes.xml で定義することで、モジュールごとに定義しています(詳細は後述)。
Magento 2 でリクエストを行うと、controller/action: index.phpを見つけるために次のような流れになります:index.php → HTTP アプリ → FrontController → Routing → Controller Processing → etc
FrontControllerはHttpクラスで呼び出され、コントローラとアクションが一致するリクエストをルーティングします。
ファイル: vendor/magento/framework/App/FrontController.php
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);
$this->response->setNoCacheHeaders();
if ($actionInstance instanceof \Magento\Framework\App\Action\AbstractAction) {
$result = $actionInstance->dispatch($request);
} else {
$result = $actionInstance->execute();
}
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;
}
この dispatch() メソッドを見ればわかるように、ルーターリストはループして、このリクエストにマッチするものを探します。このリクエストに対応するコントローラのアクションが見つかれば、そのアクションが呼び出されて実行されます。
2. フロントエンド/adminでカスタムルートを作成
本編では、シンプルなモジュールSmartosc_HelloWorldを使用します。
フロントエンドのルート
Routes.xml
フロントエンドのルートを登録するには、routes.xmlファイルを作成する必要があります。
ファイル:app/code/Smartosc/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="Smartosc_HelloWorld"/>
</route>
</router>
</config>
コードを見てみてください。ルートを登録するのはとても簡単なことだとわかります。フロントエンドには標準的なルーターを使用する必要があります。このルートは、モジュールを定義する子と、2つの属性を持ちます:
- id属性は、このルートを識別するためのユニークな文字列です。この文字列を使って、このモジュールのアクションのレイアウトハンドルを宣言します。
- frontName属性は、URLリクエストに表示される一意の文字列です。例えば、次のようなルートを宣言したとします:
<route frontName="helloworld" id="helloworld">
このモジュールのURLは次のようになります:
http://example.com/index.php/helloworld/controller/action
そして、このアクションのレイアウトハンドルは、helloworld_controller_action.xml ですので、この例のパスでは、このフォルダにアクションクラスを作成する必要があります。{namespace}/{module}/Controller/{Controller}/{Action}.php
管理ルート
このルートはフロントエンドのルートと同じになりますが、adminhtmlフォルダでルータIDをadminにして宣言しなければなりません。
ファイル:app/code/Smartosc/HelloWorld/etc/adminhtml/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 'admin' for admin route -->
<router id="admin">
<!--Define a custom route with id and frontName -->
<route id="Smartosc_helloworld" frontName="Smartosc_helloworld">
<!--The module which this route match to-->
<module name="Smartosc_HelloWorld"/>
</route>
</router>
</config>
管理ページのURLは、フロントエンドページと同じ構造ですが、admin_areaの名前がroute_frontNameの前に追加され、これが管理ルーターであることを認識します。例えば、admin cmsページのURLです:
http://example.com/index.php/admin/Smartosc_helloworld/controller/action
管理画面用のコントローラアクションは、Controller/Adminhtmlフォルダの中に追加されます。例えば、上記のURLの場合、以下のようになります。
{namespace}/{module}/Controller/Adminhtml/{Controller}/{Action}.php
ルートを使ってコントローラを書き換える
このパスでは、ルータを使ってコントローラを書き換える方法を見ていきます。上のパスのように、各ルートは識別のためのid属性を持っていることがわかります。では、もし同じid属性を持つ2つのルートを定義したらどうなるでしょうか?
答えは、コントローラのアクションが両方のモジュールに存在することになります。Magento システムはモジュールのソート順を設定するために before/after 属性を提供し、どのモジュールのコントローラが最初に見つかるかを定義します。これがコントローラの書き換えのためのロジックです。
例えば、コントローラ customer/account/login を書き換えたい場合は、以下のように route.xml でルートを定義します。
ファイル: app/code/Smartosc/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="Smartosc_HelloWorld"/>
</route>
<route id="customer">
<module name="Smartosc_HelloWorld" before="Magento_Customer" />
</route>
</router>
</config>
コントローラーファイル: app/code/Smartosc/HelloWorld/Controller/Account/Login.php
つまり、frontControllerはまずモジュールのLoginアクションを見つけ、見つかればそれを実行し、Magento_CustomerのLoginアクションは実行しないということになります。これでコントローラの書き換えに成功しました。
この方法は、他のモジュールと同じルータを持つ第2のモジュールを作るのにも使えます。例えば、上記の宣言では、コントローラのアクションにルート ‘customer’ を使うことができます。もし、コントローラ「Blog」とアクション「Index.php」があれば、このURLを使うことができます。
http://example.com/customer/blog/index