上一个问题的延续:Custom lithium routing scenario
注意:这是特定于Lithium Framework
问题
我继承了一个URL方案,它实际上没有任何约定来区分页面,项目和类别。所以,我有一个非常通用的路由器传递给一个全能控制器。
这个全能控制器(PagesController)使用url作为密钥从数据库中检索页面类型。然后PagesController运行方法并根据页面类型选择模板。我将这些信息无限期地存储在Memcached中,因此查找速度非常快。
然而,随着更多页面类型的发挥,我可以看到这个控制器变得过于臃肿和不灵活。理想情况下,我想将不同的页面类型分解为自己的控制器。
解决方案?
是否可以使用检查数据库以确定正确控制器的路由方案?
我的第一个想法是继承lithium\net\http\Router
并在Router::connect()
和Router::_parseController()
中使用自定义逻辑。
如果可以在bootstrap \ routes.php中查询数据库并根据结果创建新的lithium\net\http\Route
对象,那将是很好的。然后,只需将其传递给Router::connect()
。这似乎是一个可怕的黑客。
无论哪种方式,Router::connect()
在其设计中并不意味着 动态。
答案 0 :(得分:3)
很难非常具体,没有看到更多不同页面类型和其他示例网址的例子(我确实看过上一个问题,但我觉得这并不能说明整个情况),但与在上一个问题中,听起来答案将再次成为使用处理程序的自定义路由。
Router::connect('/{:pageType}/{:pageKey}', ['pageKey' => null], function($req) {
$types = ['list', 'of', 'page', 'types'];
$controller = 'pages';
$action = 'view';
if (in_array($req->pageType, $types)) {
// It's a proper type, do a database lookup and set
// `$controller` accordingly
// Here I'm assuming category vs. item view for the action:
$action = $req->pageKey ? 'view' : 'index';
} elseif (!$req->pageKey) {
// It's a `/custom` URL, figure out what to do with it
}
// This lets you return custom, arbitrary parameters that Lithium
// will use for dispatch
return compact('controller', 'action') + $req->params;
});
正如您所看到的,使用处理程序,您可以随心所欲地执行任何操作。解析参数,运行数据库调用,并传回一组完全任意的路由参数,供Lithium调度。
您可能想要添加到上面的唯一其他内容是一个单独的类,可以管理页面类型/自定义页面和路由参数之间的配对,如果规则开始变得复杂。
答案 1 :(得分:-1)
您可以构建一个像堆栈过滤器一样工作的路由器。
从列表顶部开始,询问每个过滤器是否与给定的URL匹配。如果它不匹配,它将一直向下继续到下一个过滤器等。
堆栈顶部的过滤器具有更高的优先级。优先级可能是因为匹配规则或仅仅是性能。即。数据库查找速度很慢,因此请添加一个过滤器,尝试尽快拒绝不匹配的URL。
您可以构建一个非常通用的Router类,您可以在其中添加过滤器。路由器只是(堆栈)过滤器的容器,具有使用给定输入数据集开始运行的方法。
class Router {
public function addFilter(Filter $filter) {}
public function run($input) {} // returns a route
}
interface Filter {
public function findRoute($input) {} // returns false or a route
}
这允许非常模块化的结构。您可以在加载模块时动态添加过滤器。
示例过滤器可以是:
class NewsFilter implements Filter {
public function findRoute($input) {
if (preg_match(":^/news/item/([0-9]+)$:", $input["url"], $matches)) {
$item = $this->news->findItem($matches[1]);
if (false === $item) {
return false;
} else {
return new NewsRoute($item);
}
}
return false;
}
}
$router = new Router();
$newsfilter = new NewsFilter();
$router->addFilter($newsfilter);
...
$router->run($input);