Features

Route groups

During development, developers often need to create sets of visually similar routes. The traditional way is creating each route, one by one, like in the below example:

$app->post('/product/create', CreateProductHandler::class, 'product:create');
$app->delete('/product/delete/{id}', DeleteProductHandler::class, 'product:delete');
$app->patch('/product/update/{id}', UpdateProductHandler::class, 'product:update');
$app->get('/product/view/{id}', GetProductHandler::class, 'product:view');

Besides the features provided by mezzio/mezzio-fastroute, dot-router offers route groups, which are collections of routes that (partially) share the same path.

To use this feature, first get an instance of the Dot\Router\RouteCollector using the below code:

/** @var \Dot\Router\RouteCollectorInterface $routeCollector */
$routeCollector = $container->get(\Dot\Router\RouteCollectorInterface::class);

Then rewrite the above routes using the below code:

$routeCollector->group('/product')
    ->post('/create', CreateProductHandler::class, 'product:create');
    ->delete('/delete/{id}', DeleteProductHandler::class, 'product:delete');
    ->patch('/update/{id}', UpdateProductHandler::class, 'product:update');
    ->get('/view/{id}', GetProductHandler::class, 'product:view');

Note that /product becomes the group prefix and the routes only specify part that is specific only to them. When dot-router registers the routes, it will automatically prepend the prefix to each route path.

Advantages

  • Dry

    no need for repeating common route parts

  • encapsulation: similar routes are grouped in a single block of code (vs. each route a separate statement)
  • easy path refactoring: modify all routes at once by changing only the prefix
  • easy copying/moving: copying/moving an entire group makes sure that you don't accidentally omit a route

Exclude middleware

Besides sharing the same path, sometimes routes may need to share handlers/middlewares too. A good example is the usage presence of CheckOwnerMiddleware in the below example:

$app->post('/product/create', CreateProductHandler::class, 'product:create');
$app->delete('/product/delete/{id}', [CheckOwnerMiddleware::class, DeleteProductHandler::class], 'product:delete');
$app->patch('/product/update/{id}', [CheckOwnerMiddleware::class, UpdateProductHandler::class], 'product:update');
$app->get('/product/view/{id}', [CheckOwnerMiddleware::class, GetProductHandler::class], 'product:view');

Just like in the first example, the routes are similar but this time there is CheckOwnerMiddleware, a middleware used by three out of the four routes.

For such cases, dot-router provides for specific routes the ability to exclude middleware from their pipeline.

Using this feature, we can rewrite the above example like this:

$routeCollector->group('/product', CheckOwnerMiddleware::class)
    ->post('/create', CreateProductHandler::class, 'product:create', CheckOwnerMiddleware::class)
    ->delete('/delete/{id}', DeleteProductHandler::class, 'product:delete')
    ->patch('/update/{id}', UpdateProductHandler::class, 'product:update')
    ->get('/view/{id}', GetProductHandler::class, 'product:view');

Note the second argument of the group method is CheckOwnerMiddleware::class. This way all group middlewares will be prepended to the route's handler, so this is the equivalent of using:

$app->post('/product/create', [CheckOwnerMiddleware::class, CreateProductHandler::class], 'product:create')

Advantages

  • extends the DRYness of route groups by allowing to specify common middlewares only once per route
  • shorter and easier-to-read route definitions