Brain-WP / Cortex

Routing system for WordPress
MIT License
347 stars 20 forks source link

Route middleware #13

Closed chrisvanpatten closed 8 years ago

chrisvanpatten commented 8 years ago

Is it possible to register route middleware? I know it's possible to add the 'before' callback, but can these be chained?

I'd like to add an auth check (if user is logged out, redirect to login page) to a big group of routes, but still need to be able to use the 'before' callback for other purposes on some routes.

Thanks!

gmazzap commented 8 years ago

Hi @chrisvanpatten

Well, it is not a Cortex feature and I think it's out of the scope of a router to make middlewares available.

With a little bit of code you can use Cortex with external libraries that provide middlewares.

To give you some directions, I think you can create you custom route class that accepts middlewares.

For example:

namespace Chris;

class MiddlewareQueryRoute extends \Brain\Cortex\Route\QueryRoute {

   private $middlewares = [];

   public function __construct($path, $queryBuilder, array $options = []) {

        // store a "before" callback if it was already there
        $before = empty($options['before']) ? null : $options['before'];

        // then set before option to a function that 1st runs middlewares and
        // after that runs the "before" callback if there was one
        $options['before'] = function($vars, $wp, $template, $matches) use($before) {
             $this->runMiddlewares($vars, $wp, $template, $matches);
             is_callable($before) and $before($vars, $wp, $template, $matches);
         };

        parent::__construct($path, $queryBuilder, $options);
  }

   public function addMiddleware(callable $middleware) {
       $this->middlewares[] = $middlewares;
       return $this;
   }

  private function runMiddlewares($vars, $wp, $template, $matches) {
       array_walk(
           $this->middlewares,
           function(callable $middleware) use($vars, $wp, $template, $matches) {
               $middleware($vars, $wp, $template, $matches);
           }
       );
  }
}

Having this class in place, you can create an instance of your route instead of Cortex QueryRoute and use it to add middlewares, something like:

use Brain\Cortex\Route\RouteCollectionInterface as Routes;
use Chris\MiddlewareQueryRoute;

add_action('cortex.routes', function(Routes $routes) {

    $path = '{type:[a-z]+}/latest';

    $querybuilder = function(array $matches) {
           return [ 'post_type' => $matches['type'] ];
    };

    $before = function($vars, $wp, $template, $matches) {
          /* do something */
    };

    $route = new MiddlewareQueryRoute($path, $querybuilder, ['before' => $before])
           ->addMiddleware(function($vars, $wp, $template, $matches) {
                /* do something */ 
           })->addMiddleware(function($vars, $wp, $template, $matches) {
               /* do something */
           });

    $routes->addRoute($route);
});

The "before" callback, if given, will be called after all the middleware callables have been ran.

Note that move from a code like this to a more sophisticated middleware implementation that makes use of, for example, "Relay" or any other middleware library out there, will not be that hard.

chrisvanpatten commented 8 years ago

Brilliant, thank you!