有关重构自定义路由方案(锂框架)的建议



上一个问题的延续:定制锂缆布线方案

注意:这是特定于锂框架的

问题所在

我继承了一个 URL 方案,它实际上没有任何约定来区分页面、项目和类别。所以,我有一个非常通用的路由器,可以传递到一个包罗万象的控制器。

此捕获所有控制器 (PagesController) 使用 url 作为从数据库中检索页面类型的键。然后,页面控制器运行方法并根据页面类型选择模板。我将此信息无限期地存储在Memcached中,因此查找速度非常快。

但是,随着更多的页面类型发挥作用,我可以看到这个控制器变得过于臃肿和不灵活。理想情况下,我想将不同的页面类型分解到它们自己的控制器中。

解决方案是什么?

是否可以有一个路由方案来检查数据库以确定正确的控制器?

我的第一个想法是子类化lithiumnethttpRouter并在Router::connect()Router::_parseController()中使用自定义逻辑。

如果可以在引导\路由中查询数据库.php并根据结果创建一个新的lithiumnethttpRoute对象,那就太好了。然后,只需将其传递给Router::connect().这似乎是一个可怕的黑客。

无论哪种方式,Router::connect()设计并不意味着要那么动态。

很难非常具体,没有看到更多不同页面类型和其他示例 URL 的示例(我确实看过上一个问题,但我感觉这并不能给出全貌),但与上一个问题一样,听起来答案将再次是带有处理程序的自定义路由。

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 调度。

您可能想要添加到上述内容的唯一其他内容是一个单独的类,如果您的规则开始变得复杂,它可以管理页面类型/自定义页面和路由参数之间的配对。

您可以构建一个像一堆过滤器一样工作的路由器。

您从列表顶部开始,询问每个过滤器是否与给定的 URL 匹配。如果不匹配,它将继续到下一个过滤器等,一直沿堆栈向下。

堆栈顶部的筛选器具有更高的优先级。优先级可能是因为匹配规则或仅仅是性能。即。数据库查找速度很慢,因此请添加一个预过滤器,以尝试尽快拒绝不匹配的 URL。

您可以构建一个非常通用的路由器类,您可以在其中添加过滤器。路由器只是(堆栈)过滤器的容器,其中包含使用给定的输入数据集开始运行的方法。

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

最新更新