在Magento中自定义路由器
当我们想要分开同一路由的一些业务逻辑而又不想重定向到控制器里的其它路由时,创建自定义路由器是非常有的。
为什么我们不为不同的逻辑创建分开的独立的路由呢?
我来稍微解释下:我结合一个我们项目中的OneStepCheckout插件(已经重写了Core Checkout路由)来谈下。我需要重写路由,根据URL中的字段将它们分到两个不同的控制器中。我发现最好的方法就是创建一个新的路由器重定向到由字段决定的不同控制器。
在“…/app/code/local/Mynamespace/Myextension/etc/config.xml”创建我们自己的路由器
<default>
<web>
<routers>
<myextension_myrouter>
<area>frontend</area>
<class>Mynamespace_Myextension_Controller_Router</class>
</myextension_myrouter>
</routers>
</web>
</default>
</stores>
在 “…/app/code/local/Mynamespace/Myextension”新建Controller文件夹,在文件夹里新建Mynamespace_Myextension_Controller_Router.php文件:
<?php
//Mynamespace_Myextension_Controller_Router.php
/**
* Router for deciding to go on party checkout or regular onestepcheckout
*/
class Mynemaspace_Myextension_Controller_Router extends
Mage_Core_Controller_Varien_Router_Standard {
}
?>
拷贝Mage_Core_Controller_Varien_Router_Standard.php中对应的方法到我们的类,进行修改:
/**
* Match the request
*
* @param Zend_Controller_Request_Http $request
* @return boolean
*/
public function match(Zend_Controller_Request_Http $request)
{
//checking before even try to find out that current module
//should use this router
if (!$this->_beforeModuleMatch()) {
return false;
}
$this->fetchDefault();
$front = $this->getFront();
$path = trim($request->getPathInfo(), '/');
if ($path) {
$p = explode('/', $path);
} else {
$p = explode('/', $this->_getDefaultPath());
}
// get module name
if ($request->getModuleName()) {
$module = $request->getModuleName();
} else {
if (!empty($p[0])) {
$module = $p[0];
} else {
$module = $this->getFront()->getDefault('module');
$request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS, '');
}
}
if (!$module) {
if (Mage::app()->getStore()->isAdmin()) {
$module = 'admin';
} else {
return false;
}
}
/**
* Searching router args by module name from route using it as key
*/
$modules = $this->getModuleByFrontName($module);
if ($modules === false) {
return false;
}
//checkings after we foundout that this router should be used for current module
if (!$this->_afterModuleMatch()) {
return false;
}
/**
* Going through modules to find appropriate controller
*/
$found = false;
foreach ($modules as $realModule) {
$request->setRouteName($this->getRouteByFrontName($module));
// get controller name
if ($request->getControllerName()) {
$controller = $request->getControllerName();
} else {
if (!empty($p[1])) {
$controller = $p[1];
} else {
$controller = $front->getDefault('controller');
$request->setAlias(
Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS,
ltrim($request->getOriginalPathInfo(), '/')
);
}
}
// get action name
if (empty($action)) {
if ($request->getActionName()) {
$action = $request->getActionName();
} else {
$action = !empty($p[2]) ? $p[2] : $front->getDefault('action');
}
}
//checking if this place should be secure
$this->_checkShouldBeSecure($request, '/'.$module.'/'.$controller.'/'.$action);
$controllerClassName = $this->_validateControllerClassName($realModule, $controller);
if (!$controllerClassName) {
continue;
}
// instantiate controller class
$controllerInstance = Mage::getControllerInstance($controllerClassName, $request,
$front->getResponse());
if (!$controllerInstance->hasAction($action)) {
continue;
}
$found = true;
break;
}
/**
* if we did not found any siutibul
*/
if (!$found) {
if ($this->_noRouteShouldBeApplied()) {
$controller = 'index';
$action = 'noroute';
$controllerClassName = $this->_validateControllerClassName($realModule,
$controller);
if (!$controllerClassName) {
return false;
}
// instantiate controller class
$controllerInstance = Mage::getControllerInstance($controllerClassName, $request,
$front->getResponse());
if (!$controllerInstance->hasAction($action)) {
return false;
}
} else {
return false;
}
}
// set values only after all the checks are done
$request->setModuleName($module);
$request->setControllerName($controller);
$request->setActionName($action);
$request->setControllerModule($realModule);
// set parameters from pathinfo
for ($i = 3, $l = sizeof($p); $i < $l; $i += 2) {
$request->setParam($p[$i], isset($p[$i+1]) ? urldecode($p[$i+1]) : '');
}
// dispatch action
$request->setDispatched(true);
$controllerInstance->dispatch($action);
return true;
}
例如,现在我想重定向路由到“Party”控制器。我们可以给控制器名添加前缀。一般控制器是:Mynamespace/Myextension/controllers/Somecontrollername.php,“Party”控制器就是:Mynamespace/Myextension/controllers/Party/Somecontrollername.php。要实现这个,那么我们需要在上面代码的57行到62行之间添加些代码:
$party_prefix = '';
if ($this->isPartyOrder($request)) {
foreach ($modules as $key => $realModule) {
$modules[$key].='_Party';
}
$party_prefix = '_party';
}
当然,我们的isParty方法是这样的:
..
/**
* Determine if order is "Party order"
* @param Mage_Core_Controller_Request_Http $request
* @return boolean
*/
private function isPartyOrder($request) {
if (isset($_SESSION['is_party_checkout']) && $_SESSION['is_party_checkout']) {
return true;
}
$pathInfo = $request->getPathInfo();
if (stristr($pathInfo, 'onestepcheckout/index/index')) {
$params = Mage::app()->getRequest()->getParams();
if (isset($params['id'])) {
return true;
}
}
return false;
}
..
这里,我们检查Party是否在session或者URL的GET参数里。
下一步,让我们修改一个将被layout.xml文件使用的路由名,替换这段代码
/**
* Going through modules to find appropriate controller
*/
$found = false;
foreach ($modules as $realModule) {
$request->setRouteName($this->getRouteByFrontName($module));
为
//...
/**
* Going through modules to find appropriate controller
*/
$found = false;
foreach ($modules as $realModule) {
$request->setRouteName('ourextension_' . $this->getRouteByFrontName($module) .
$party_prefix);
...
//...
现在我们可以在布局xml文件里定义布局句柄来将“Party” 和其它路由区分开。
<?xml version="1.0"?>
<layout version="0.1.0">
<<ourmodule_controllername_actionname>
<block ....... some layout updates ... etc
</ourmodule_controllername_actionname>
<ourmodule_party_controllername_actionname>
<block ....... some different layout updates ... etc
</ourmodule_party_controllername_actionname>
</layout>
这样我们就可以单独实现两种完全不同的布局。这只是个简单的例子来创建和修改路由器。你可以定制自己的路由器来实现你的需求。
下面让我们看看onestepcheckout的config.xml文件,看它的路由器是如何设置模块的:
<frontend>
<routers>
<onestepcheckout>
<use>myextension_myrouter</use>
<args>
<modules>
<Mynamespace_Mymodule before="Idev_OneStepCheckout">Mynamespace_Mymodule_Onestepcheckout</Mynamespace_Mymodule>
</modules>
</args>
</onestepcheckout>
</routers>
Onestepcheckout模块重写一些Magento核心路由,这样当客户访问checkout/onepage地址时,他们会得到 onestepcheckout地址。
当我们创建像上面的自定义路由器时,客户仍然会访问onestepcheckout路由,但会根据isParty的判断结果来执行不同的控制器。
由于同样的URL分为Party和regular支付,我们在路由里为每个支付状态(Party and regular) 设置自定义路由名而更新xml句柄。
虽然第一次读起来可能会有些乱,但我希望在某些人的项目,插件和定制中起到作用。
电商网站开发服务。