【问题标题】:Advice on Refactoring Custom Routing Scenario (Lithium Framework)关于重构自定义路由场景的建议(锂框架)
【发布时间】:2013-01-28 17:07:37
【问题描述】:

上一个问题的延续: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()。这似乎是一个可怕的 hack。

无论如何,Router::connect() 在其设计中并不意味着 是动态的。

【问题讨论】:

    标签: php refactoring url-routing lithium


    【解决方案1】:

    很难非常具体,没有看到更多不同页面类型的示例和其他示例 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 调度。

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

    【讨论】:

    • 谢谢内特!我不知道我可以直接在处理程序中修改控制器和操作。我想我必须创建一个新的 Route 对象。
    • 另外一个问题,handler是建议返回参数还是原来传入的Request对象?
    • 一般来说,你可以只返回参数(或者只返回true,如果你不修改路由的内容)。 Dispatcher 负责对它们进行后处理,并将它们合并到 Request 对象本身中。
    【解决方案2】:

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

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

    堆栈顶部的过滤器具有更高的优先级。优先级可能是因为匹配规则或仅仅是性能。 IE。数据库查找速度很慢,因此请添加一个预过滤器,以尽快拒绝不匹配的 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);
    

    【讨论】:

    • 感谢 Frits,但这个问题是特定于锂框架的。 Lithium 提供了一个路由接口,可以执行您所描述的操作。
    • 然后按照我描述的方式使用?
    • 也许我的问题并不清楚,但我的 URL 方案并没有任何约定来区分页面、项目和类别。这就是这些类型存储在数据库中的原因
    猜你喜欢
    • 1970-01-01
    • 2023-03-26
    • 2016-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 2022-12-04
    相关资源
    最近更新 更多