Skip to Content
ModulesRouter API

Router API

The Flute CMS Router API is built on top of Symfony Routing with additional capabilities. It supports both attribute routing and programmatic route creation.

Getting the Router

<?php use Flute\Core\Router\Router; // Via DI container $router = app(Router::class); // Via helper $router = router();

Creating Routes

Basic HTTP Methods

<?php // GET request router()->get('/users', [UserController::class, 'index']); // POST request router()->post('/users', [UserController::class, 'store']); // PUT request router()->put('/users/{id}', [UserController::class, 'update']); // DELETE request router()->delete('/users/{id}', [UserController::class, 'destroy']);

Additional Methods

<?php // Multiple HTTP methods router()->match(['GET', 'POST'], '/contact', [ContactController::class, 'handle']); // Any HTTP method router()->any('/webhook', [WebhookController::class, 'handle']); // Static pages (returns view) router()->view('/about', 'pages.about', ['title' => 'About Us']); // Redirect router()->redirect('/old-url', '/new-url', 301);

Named Routes

<?php router()->get('/news', [NewsController::class, 'index']) ->name('news.index'); router()->get('/news/{id}', [NewsController::class, 'show']) ->name('news.show'); // Using name to generate URL $url = route('news.show', ['id' => 123]); // Result: /news/123

Middleware

<?php // Single middleware router()->post('/news', [NewsController::class, 'store']) ->middleware('auth') ->name('news.store'); // Multiple middleware router()->put('/news/{id}', [NewsController::class, 'update']) ->middleware(['auth', 'csrf', 'can:edit_news']) ->name('news.update');

Route Groups

<?php // Group with common prefix and middleware router()->group(['prefix' => '/admin', 'middleware' => ['auth', 'admin']], function () { router()->get('/news', [AdminNewsController::class, 'index']); router()->post('/news', [AdminNewsController::class, 'store']); router()->get('/news/{id}', [AdminNewsController::class, 'edit']); }); // Nested groups router()->group(['prefix' => '/api'], function () { router()->group(['prefix' => '/v1', 'middleware' => 'throttle:100,1'], function () { router()->get('/users', [ApiUserController::class, 'index']); router()->get('/users/{id}', [ApiUserController::class, 'show']); }); });

Attribute Routing

Automatic Loading

Routes from attributes are loaded automatically when calling bootstrapModule() or loadRouterAttributes() in the provider:

<?php // In provider public function boot(\DI\Container $container): void { $this->bootstrapModule(); // Includes loadRouterAttributes() }

Manual Registration

<?php // Register from directory $router->registerAttributeRoutes( [app_path('Modules/News/Controllers')], 'Flute\\Modules\\News\\Controllers' ); // Register from specific class $router->registerAttributeRoutesFromClass(NewsController::class);

Built-in Middleware

Aliases

AliasClassDescription
authIsAuthenticatedMiddlewareAuthentication check
guestIsGuestMiddlewareOnly for guests
canCanMiddlewarePermission check
csrfCsrfMiddlewareCSRF protection
htmxHtmxMiddlewareHTMX requests
throttleRateLimiterMiddlewareRate limiting
tokenTokenMiddlewareAPI tokens
ban.checkBanCheckMiddlewareBan check
maintenanceMaintenanceMiddlewareMaintenance mode

Groups

<?php // Built-in middleware groups 'web' => ['csrf', 'throttle'], 'api' => ['throttle', 'ban.check'], 'default' => ['ban.check', 'throttle', 'maintenance'],

The default group is added to all routes automatically.

Middleware with Parameters

<?php // Check permission router()->get('/admin/users', [AdminController::class, 'users']) ->middleware('can:admin.users'); // Rate limiting: 100 requests per minute router()->get('/api/data', [ApiController::class, 'data']) ->middleware('throttle:100,1');

Registering Custom Middleware

Registering Alias

<?php // Registering custom middleware router()->aliasMiddleware('blog.owner', BlogOwnerMiddleware::class); // Usage router()->put('/blog/{id}', [BlogController::class, 'update']) ->middleware('blog.owner');

Registering Group

<?php router()->middlewareGroup('blog', [ 'auth', 'blog.owner', 'throttle:10,1' ]); // Using group router()->group(['middleware' => 'blog'], function () { router()->put('/blog/{id}', [BlogController::class, 'update']); router()->delete('/blog/{id}', [BlogController::class, 'destroy']); });

Creating Middleware

<?php namespace Flute\Modules\Blog\Middlewares; use Closure; use Flute\Core\Router\Contracts\MiddlewareInterface; use Flute\Core\Support\FluteRequest; use Symfony\Component\HttpFoundation\Response; class BlogOwnerMiddleware implements MiddlewareInterface { /** * Handle request * * @param FluteRequest $request Current request * @param Closure $next Next handler * @param mixed ...$args Additional arguments */ public function handle(FluteRequest $request, Closure $next, ...$args): Response { // Get article ID from route parameters $blogId = $request->getAttribute('id'); if (!$blogId) { return response()->error(400, 'Article ID not specified'); } // Check existence and owner $blog = \Flute\Modules\Blog\Database\Entities\Article::findByPK($blogId); if (!$blog) { return response()->error(404, 'Article not found'); } if ($blog->author_id !== user()->id && !user()->can('admin.blog')) { return response()->error(403, 'Access denied'); } // Pass control further return $next($request); } }

URL Generation

Main Methods

<?php // Via helper (recommended) $url = route('news.show', ['id' => 123]); // Via router $url = router()->url('news.show', ['id' => 123]);

Examples

<?php // Simple route without parameters $homeUrl = route('home'); // Result: / // With parameter in path $newsUrl = route('news.show', ['id' => 123]); // Result: /news/123 // With multiple parameters $articleUrl = route('blog.article', ['category' => 'php', 'slug' => 'my-article']); // Result: /blog/php/my-article // With query parameters $searchUrl = route('search') . '?' . http_build_query(['q' => 'flute', 'page' => 2]); // Result: /search?q=flute&page=2

Check Existence

<?php // Check existence of route by path if (router()->hasRoute('/news', ['GET'])) { echo "Route exists"; } // Check by name if (router()->getRoutes()->get('news.show')) { $url = route('news.show', ['id' => 123]); }

Helper Methods

<?php // Get current route $currentRoute = router()->getCurrentRoute(); // Get all routes $allRoutes = router()->getRoutes();

Routing Events

Router dispatches events during processing:

<?php use Flute\Core\Router\Events\RoutingStartedEvent; use Flute\Core\Router\Events\OnRouteFoundEvent; use Flute\Core\Router\Events\RoutingFinishedEvent; // On router initialization events()->addListener(RoutingStartedEvent::NAME, function($event) { // Router started processing }); // On finding route events()->addListener(OnRouteFoundEvent::NAME, function($event) { $route = $event->getRoute(); $request = $event->getRequest(); // Can modify request or add logic }); // After processing request events()->addListener(RoutingFinishedEvent::NAME, function($event) { // Router finished processing });

Caching

Router automatically caches compiled routes:

storage/app/cache/routes_compiled_front.php - frontend routes storage/app/cache/routes_compiled_admin.php - admin routes

In debug mode, cache is automatically invalidated. In production, cache persists until route files change.

Best Practices

Name Routes

Always use clear names with dot notation:

->name('news.show') // Good ->name('show_news') // Bad

Group by Module

Start names with module name to avoid conflicts.

Use Middleware

Always add permission checks and CSRF protection:

router()->post('/news', [NewsController::class, 'store']) ->middleware(['auth', 'csrf', 'can:create_news']);

Structure Groups Logically

router()->group(['prefix' => '/news'], function () { // Public routes router()->get('/', [NewsController::class, 'index'])->name('news.index'); router()->get('/{id}', [NewsController::class, 'show'])->name('news.show'); // Protected routes router()->group(['middleware' => 'auth'], function () { router()->post('/', [NewsController::class, 'store'])->name('news.store'); router()->put('/{id}', [NewsController::class, 'update'])->name('news.update'); }); });

Minimize Middleware

For frequently used routes, avoid redundant middleware.