Is there a way to use addGroup from FastRoute?

Hi,

I’m trying to use the method FastRoute\ConfigureRoutes::addGroup to create a group of routes. However, I noticed that this method is not exposed in the Mezzio\Router\RouterCollectorInterface.

Is there a way to achieve route grouping in Mezzio? Any guidance would be appreciated!

Thanks!

Hello and welcome to our forums! :smiley:

This is correct because other router solution does not have this method and the interface reduces these to the relevant methods.
You can override the factory but a simple loop in a config provider class can create the same result.

Another solution can be find here:

Hi,

I appreciate the warm welcome. :grinning_face:

The reason I was looking at grouping routes is that I want to apply the same middleware to every route in a group. For example, I’ve been considering an approach like this:

<?php

declare(strict_types=1);

namespace Admin\Middleware;

use Mezzio\Router\RouteResult;
use Override;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

use function str_starts_with;

final readonly class AdminMiddleware implements MiddlewareInterface
{
    public function __construct(
        private AllowOnlyAdminMiddleware $alternative,
    ) {
    }

    #[Override]
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        /** @var ?RouteResult $routeResult */
        $routeResult = $request->getAttribute(RouteResult::class);
        if (str_starts_with($routeResult->getMatchedRouteName(), 'admin')) {
            return $this->alternative->process($request, $handler);
        }

        return $handler->handle($request);
    }
}

Another approach I see now is building on what you provided:

public function registerRoutes(Application $app, string $basePath = '/blog'): void
{
    $groupMiddleware = [Middleware\GroupMiddleware::class];

    $app->get($basePath . '[/]', [...$groupMiddleware, Handler\ListPostsHandler::class], 'blog');
    $app->get($basePath . '/{id:[^/]+}.html', [...$groupMiddleware, Handler\DisplayPostHandler::class], 'blog.post');
    $app->get($basePath . '/tag/{tag:php}.xml', [...$groupMiddleware, Handler\FeedHandler::class], 'blog.feed.php');
    $app->get($basePath . '/{tag:php}.xml', [...$groupMiddleware, Handler\FeedHandler::class], 'blog.feed.php.also');
    $app->get($basePath . '/tag/{tag:[^/]+}/{type:atom|rss}.xml', [...$groupMiddleware, Handler\FeedHandler::class], 'blog.tag.feed');
    $app->get($basePath . '/tag/{tag:[^/]+}', [...$groupMiddleware, Handler\ListPostsHandler::class], 'blog.tag');
    $app->get($basePath . '/{type:atom|rss}.xml', [...$groupMiddleware, Handler\FeedHandler::class], 'blog.feed');
    $app->get($basePath . '/search[/]', [...$groupMiddleware, Handler\SearchHandler::class], 'blog.search');
}

Does this approach align with how grouping and middleware are typically handled in Mezzio? Or is there a more conventional way to do this?

FWIW, I will normally create an empty interface such as MySpecificPipeline and map that to a factory in configuration such as

interface MySpecificPipeline extends MiddlewarePipeInterface {}

final readonly class MyPipelineFactory {
  public function __invoke(ContainterInterface $container): MiddlewarePipeInterface {
    $factory = $container->get(MiddlewareFactoryInterface::class);
    
    return $factory->pipeline([
      Middleware1::class,
      Middleware2::class,
    ]);
  }
}

Anytime I need to use that specific ‘chunk’ of middlewares, I can then define a route to include that pipeline.

Stuff that will always execute on every request is better off in the application pipeline config though, i.e. config/pipeline.php.

You can also consider adding entire “pipelines” using “path segregation” - Routing vs Piping - mezzio - Laminas Docs

1 Like