Middleware pipelines improvements - markstory/cakephp GitHub Wiki

As the first part of the middleware pipeline is now implemented, we should look on how to improve it even further. A common use case would be, to use an already registered middleware again in a more specific scope.

Following is a rough sketch on how the api for that could look like.

Router::scope('/', function (RouteBuilder $routes) {

    $routes->registerMiddleware('cookie', new CookieMiddleware(/* config */));
    $routes->registerMiddleware('auth', new AuthMiddleware(/* config */));
    $routes->registerMiddleware('users', new UsersMiddleware(/* config */));

    // Maybe middleware groups
    $routes->registerMiddlewareGroup('web', ['cookie', 'auth']);

    //  ... or
    $routes->registerMiddlewareGroup('web', [new CookieMiddleware(/* config */), new AuthMiddleware(/* config */)]);

    // Apply the cookie and auth middleware to the complete `/` scope.
    $routes->applyMiddleware('cookie');
    $routes->applyMiddleware('auth');

    $routes->scope('/api', function ($routes) {
          // Apply the auth middleware to the `/api` scope but with a different config as before
          $routes->applyMiddleware('auth', new AuthMiddleware(/* different config */));

          // Don't use the cookie middleware for `/api`
         $routes->applyMiddleware('cookie', ???);
    });

    // Only apply the users middleware to this route on GET requests
    $routes->get('/users', ['controller' => 'users', 'action' => 'index'])->applyMiddleware('users');

    // .. or apply multiple middleware at once
    $routes->get('/users', ['controller' => 'users', 'action' => 'index'])->applyMiddleware('users', 'cookie');

    // Maybe you want a different config for POST
    $routes->post('/users', ['controller' => 'users', 'action' => 'index'])->applyMiddleware('users', new UsersMiddleware(/* different config */));
});

Another approach would be to change how middleware and scopes work. Instead of a route inheriting the middleware from each parent scope, instead routes only have middleware defined in the closest scope applied. Take the following example:

Router::scope('/', function($routes) {
  $routes->applyMiddleware('one', 'two');
  $routes->scope('/api', function ($routes) {
    $routes->applyMiddleware('api');
    $routes->get('/ping');
  });
});

In the above example, the /api/ping route would only have the 'api' middleware applied. The 'one', 'two' middleware would not be applied. This behavior allows us to remove/redefine middleware in nested scopes. Because middleware would need to be defined in nested scopes, we should also add the concept of 'middleware groups'.

Router::scope('/', function ($routes) {
  // Add middleware
  $routes->registerMiddleware('auth', new AuthMiddleware());
  $routes->registerMiddleware('cookie', ...);
  // Define the middleware groups
  $routes->middlewareGroup('web', ['cookie', 'auth']);
  $routes->middlewareGroup('api', ['auth.api']);

  $routes->applyMiddleware('web');

  $routes->scope('/api', function ($routes) {
    // Apply the middleware in the api group. Web will not be applied.
    $routes->applyMiddleware('api');
  });
});