【问题标题】:Mapping multiple routes (controller) using a router使用路由器映射多个路由(控制器)
【发布时间】:2017-01-24 07:46:56
【问题描述】:

我正在查看 danny vankooten 路由器库 here。这看起来不错(虽然不确定它将如何处理从大中型到大型的项目,例如电子商务网站)。现在,通过这个例子,这是映射

$router->map('GET','/', 'home.php', 'home');
$router->map('GET','/home/', 'home.php', 'home-home');
$router->map('GET','/plans/', 'plans.php', 'plans');
$router->map('GET','/about/', 'about.php', 'about');
$router->map('GET','/contact/', 'contact.php', 'contact');
$router->map('GET','/tos/', 'tos.html', 'tos');

假设我的网站有 20-30 个静态页面或大约 50 个控制器,每个控制器有 2-3 个动作/方法。

我如何将它们全部映射。如果我使用上面的映射方法,我最终可能会有超过 100 行的映射,这看起来不对。

我相信应该有一种方法或捷径/通配符,例如检查是否有可用的页面或控制器,然后加载它或抛出 404。

如何正确映射所有路线?

附言。向愿意回答如何使用通配符匹配控制器/方法的上述路由器的任何人提供 50 的赏金。

【问题讨论】:

    标签: php design-patterns model-view-controller routing routes


    【解决方案1】:

    要减轻路由器文件的重量,您可以将路由定义移动到 YAML 文件中。你的 YAML 中仍然会有很多行,但它会更具可读性。

    在您的 router.php 文件中,使用以下代码:

    不要忘记将 symfony YAML 解析器添加到您的 composer.json

    use Symfony\Component\Yaml\Yaml;
    $yaml_file = 'routes.yaml';
    $routes = Yaml::parse(file_get_contents($yaml_file));
    foreach ($routes as $route_name => $params) {
        $router->map($params[0],$params[1], $params[2].'#'.$params[3], $route_name);
    } 
    
    // match current request
    $match = $router->match();
    

    您的文件routes.yaml 将如下所示

    index:      ["GET", "/", "home_controller", "display_item"]
    content:    ["GET", "/content/[:parent]/?[:child]?", "content_controller", "display_item"]
    article:    ["GET", "/article/[:page]", "article_controller", "display_item"]
    

    您可以做的另一件事是获取较小的文件,将您的路由定义分隔在许多小的 YAML 文件中。比如静态文件一个,管理区一个,前台一个……

    要做这样的事情,你必须把router.php代码改成这样:

    use Symfony\Component\Yaml\Yaml;
    $yaml_files = ['front.yaml', 'static.yaml', 'admin.yaml'];
    foreach ($yaml_files as $yaml_file) {
        $routes = Yaml::parse(file_get_contents($yaml_file));
        foreach ($routes as $route_name => $params) {
            $router->map($params[0],$params[1], $params[2].'#'.$params[3], $route_name);
        } 
    }
    
    // match current request
    $match = $router->match();
    

    Danny Van Kooten 还制作了 PHP-Router,它内置了对 YAML 文件的支持。 (如果你查看源代码,你会发现他使用的是 Symfony 解析器,所以这两种方法非常相似)

    From the doc

    YAML 路由定义

    base_path: /blog
    
    routes:
      index: [/index, someClass.indexAction, GET]
      contact: [/contact, someClass.contactAction, GET]
      about: [/about, someClass.aboutAction, GET]
    

    路由器.php

    require __DIR__.'/vendor/autoload.php';
    
    use PHPRouter\RouteCollection;
    use PHPRouter\Config;
    use PHPRouter\Router;
    use PHPRouter\Route;
    
    $config = Config::loadFromFile(__DIR__.'/router.yaml');
    $router = Router::parseConfig($config);
    $router->matchCurrentRequest();
    

    【讨论】:

      【解决方案2】:

      在库中没有可以执行您想要的操作的函数,但是有另一种方法可以执行此操作,就是将这些页面存储在数据库中,这样您将在下面得到类似的内容

      //your query
      // "SELECT `method`, `route`, `page`, `name` FROM `static_pages`
      $pages = (query); //your executed query
      if(count($pages) > 0){
        foreach($pages as $page){
          $router->map($page['method'],$page['route'], $page['target'], (($page['name'] !== null || !empty($page['name']) ) ? $page['name'] : null));
        }
      }
      

      希望对你有帮助

      【讨论】:

      • 我正在查看与您的建议相符的内容,但不确定您发布的代码...希望获得更多答案。不过还是谢谢你的建议。
      • 即使在行中也有一种方法可以创建一个数组,但这就像写很多行并加载像$pages = array( array('GET', '/', 'target.php', 'name'), array('GET', '/target', 'target2.php', 'name2'), array('GET', '/target2', 'target3.php', null) //.. and all defined arrays ); foreach($pages as $page) { $router->map($page[0],$page[1], $page[2], $page[3]); } 这样的文件一样。
      【解决方案3】:

      如果您使用像Silex 这样的预构建路由器,您可以执行以下操作:

      $app->get('{route}', function($route) {
          $dir = "/path/to/my/static/files/";
          $file = $dir . $route . '.php';
          if (file_exists($file)) {
              require $file;
          } else {
              require '/path/to/custom/404.php';
          }
      });
      

      请注意,Silex 和许多类似的路由器要求路由按优先顺序排列。当我需要使用这样的路由器时,我把它放在底部,这样它就不会意外夹到其他东西。

      请注意,如果您想构建类似这种自定义的东西,您可以这样做 - 请记住,我不知道您的代码当前是什么样子或您需要什么逻辑 - 通过替换您的正则表达式(在 Silex 的例如,它带有大括号)并根据您制定的正则表达式检查$_SERVER['REQUEST_URI']。

      如果这是一个自定义应用程序,您可能真的不需要过度设计它。

      // routing class
      foreach ($routes as $route) {
          if ($route != self::DEFAULT_ROUTE) {
              // do your normal routing logic
          } else {
              // check if file exists (as with code above), else send to error 404
          }
      }
      

      然后调用类似:

      $router->map('GET',Router::DEFAULT_ROUTE, 'legacyRouter.php', 'legacy');
      

      【讨论】:

        【解决方案4】:

        作为引导阶段的一部分,告诉您的控制器将自己映射到路由器:

        // bootstrap
        $router = new AltoRouter();
        
        FooController::mapRoutes($router);
        BarController::mapRoutes($router);
        // etc.
        

        每个控制器都可以生成它需要的任意数量的路由。

        【讨论】:

          猜你喜欢
          • 2021-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-12
          • 1970-01-01
          • 2016-03-16
          相关资源
          最近更新 更多