Browse Source

Add routes() hook.

This hook method is currently optional, but will become required in
4.0.0, and replaces the automatic route loading that Router currently
uses. To fully remove that behavior we'll need to break compatibility,
and there are a few other Router deprecations that need to be done
first.
Mark Story 8 years ago
parent
commit
5ab8de4468

+ 13 - 0
src/Http/BaseApplication.php

@@ -62,6 +62,19 @@ abstract class BaseApplication
     }
     }
 
 
     /**
     /**
+     * Define the routes for an application.
+     *
+     * By default this will load `config/routes.php` for ease of use and backwards compatibility.
+     *
+     * @param \Cake\Routing\RouteBuilder $routes A route builder to add routes into.
+     * @return void
+     */
+    public function routes($routes)
+    {
+        require $this->configDir . '/routes.php';
+    }
+
+    /**
      * Invoke the application.
      * Invoke the application.
      *
      *
      * - Convert the PSR response into CakePHP equivalents.
      * - Convert the PSR response into CakePHP equivalents.

+ 33 - 2
src/Routing/Middleware/RoutingMiddleware.php

@@ -14,9 +14,11 @@
  */
  */
 namespace Cake\Routing\Middleware;
 namespace Cake\Routing\Middleware;
 
 
+use Cake\Http\BaseApplication;
 use Cake\Http\MiddlewareQueue;
 use Cake\Http\MiddlewareQueue;
 use Cake\Http\Runner;
 use Cake\Http\Runner;
 use Cake\Routing\Exception\RedirectException;
 use Cake\Routing\Exception\RedirectException;
+use Cake\Routing\RouteBuilder;
 use Cake\Routing\Router;
 use Cake\Routing\Router;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Message\ServerRequestInterface;
@@ -29,10 +31,38 @@ use Zend\Diactoros\Response\RedirectResponse;
 class RoutingMiddleware
 class RoutingMiddleware
 {
 {
     /**
     /**
+     * Constructor
+     *
+     * @param \Cake\Http\BaseApplication $app The application instance that routes are defined on.
+     */
+    public function __construct(BaseApplication $app = null)
+    {
+        $this->app = $app;
+    }
+
+    /**
+     * Trigger the application's routes() hook if the application exists.
+     *
+     * If the middleware is created without an Application, routes will be
+     * loaded via the automatic route loading that pre-dates the routes() hook.
+     *
+     * @return void
+     */
+    protected function loadRoutes()
+    {
+        if ($this->app) {
+            $builder = Router::getRouteBuilder('/');
+            $this->app->routes($builder);
+            // Prevent routes from being loaded again
+            Router::$initialized = true;
+        }
+    }
+
+    /**
      * Apply routing and update the request.
      * Apply routing and update the request.
      *
      *
-     * Any route/path specific middleware will be wrapped around $next and then the new middleware stack
-     * will be invoked.
+     * Any route/path specific middleware will be wrapped around $next and then the new middleware stack will be
+     * invoked.
      *
      *
      * @param \Psr\Http\Message\ServerRequestInterface $request The request.
      * @param \Psr\Http\Message\ServerRequestInterface $request The request.
      * @param \Psr\Http\Message\ResponseInterface $response The response.
      * @param \Psr\Http\Message\ResponseInterface $response The response.
@@ -41,6 +71,7 @@ class RoutingMiddleware
      */
      */
     public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
     public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
     {
     {
+        $this->loadRoutes();
         try {
         try {
             Router::setRequestContext($request);
             Router::setRequestContext($request);
             $params = (array)$request->getAttribute('params', []);
             $params = (array)$request->getAttribute('params', []);

+ 1 - 0
src/Routing/Router.php

@@ -1086,6 +1086,7 @@ class Router
     /**
     /**
      * Loads route configuration
      * Loads route configuration
      *
      *
+     * @deprecated 3.5.0 Routes will be loaded via the Application::routes() hook in 4.0.0
      * @return void
      * @return void
      */
      */
     protected static function _loadRoutes()
     protected static function _loadRoutes()

+ 30 - 0
tests/TestCase/Routing/Middleware/RoutingMiddlewareTest.php

@@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\Routing\Middleware;
 use Cake\Routing\Middleware\RoutingMiddleware;
 use Cake\Routing\Middleware\RoutingMiddleware;
 use Cake\Routing\Router;
 use Cake\Routing\Router;
 use Cake\TestSuite\TestCase;
 use Cake\TestSuite\TestCase;
+use TestApp\Application;
 use Zend\Diactoros\Response;
 use Zend\Diactoros\Response;
 use Zend\Diactoros\ServerRequestFactory;
 use Zend\Diactoros\ServerRequestFactory;
 
 
@@ -105,6 +106,35 @@ class RoutingMiddlewareTest extends TestCase
     }
     }
 
 
     /**
     /**
+     * Test middleware invoking hook method
+     *
+     * @return void
+     */
+    public function testRoutesHookInvokedOnApp()
+    {
+        Router::reload();
+        $this->assertFalse(Router::$initialized, 'Router precondition failed');
+
+        $request = ServerRequestFactory::fromGlobals(['REQUEST_URI' => '/app/articles']);
+        $response = new Response();
+        $next = function ($req, $res) {
+            $expected = [
+                'controller' => 'Articles',
+                'action' => 'index',
+                'plugin' => null,
+                'pass' => [],
+                '_matchedRoute' => '/app/articles'
+            ];
+            $this->assertEquals($expected, $req->getAttribute('params'));
+            $this->assertTrue(Router::$initialized, 'Router state should indicate routes loaded');
+            $this->assertCount(1, Router::routes());
+        };
+        $app = new Application(CONFIG);
+        $middleware = new RoutingMiddleware($app);
+        $middleware($request, $response, $next);
+    }
+
+    /**
      * Test that routing is not applied if a controller exists already
      * Test that routing is not applied if a controller exists already
      *
      *
      * @return void
      * @return void

+ 13 - 0
tests/test_app/TestApp/Application.php

@@ -42,4 +42,17 @@ class Application extends BaseApplication
 
 
         return $middleware;
         return $middleware;
     }
     }
+
+    /**
+     * Routes hook, used for testing with RoutingMiddleware.
+     *
+     * @param \Cake\Routing\RouteBuilder $routes
+     * @return void
+     */
+    public function routes($routes)
+    {
+        $routes->scope('/app', function ($routes) {
+            $routes->connect('/articles', ['controller' => 'Articles']);
+        });
+    }
 }
 }