Routing
Routing
The router maps URIs to controller actions, runs middleware, and normalises responses. Implementation: core/libraries/Router/Router.php.
use Zero\Lib\Router;Every verb method returns a RouteDefinition you can chain on (->name(), ->middleware()).
Defining routes
Router::get(string $route, array $action, mixed $middlewares = null): RouteDefinition
Register a GET route.
use App\Controllers\UserController;
Router::get('/users', [UserController::class, 'index']);
Router::get('/users/{id}', [UserController::class, 'show']);Router::post(string $route, array $action, mixed $middlewares = null): RouteDefinition
Router::post('/users', [UserController::class, 'store']);Router::put(string $route, array $action, mixed $middlewares = null): RouteDefinition
Router::put('/users/{id}', [UserController::class, 'update']);Router::patch(string $route, array $action, mixed $middlewares = null): RouteDefinition
Router::patch('/users/{id}', [UserController::class, 'patch']);Router::delete(string $route, array $action, mixed $middlewares = null): RouteDefinition
Router::delete('/users/{id}', [UserController::class, 'destroy']);Path parameters
Wrap segments in {}. They're injected by name into the controller signature.
// routes/web.php
Router::get('/users/{id}/posts/{postId}', [PostController::class, 'show']);
// app/controllers/PostController.php
public function show(int $id, int $postId, Request $request) { /* ... */ }The shared Request instance is auto-injected when type-hinted.
Route groups
Router::group(array $attributes, callable $callback): void
Apply shared prefix, middleware, and name to a batch of routes.
Router::group(['prefix' => '/admin', 'middleware' => 'auth'], function () {
Router::get('/users', [AdminUserController::class, 'index'])->name('users.index');
Router::post('/users', [AdminUserController::class, 'store'])->name('users.store');
});Groups can nest:
Router::group(['prefix' => '/api/v1', 'name' => 'api.'], function () {
Router::group(['middleware' => 'auth'], function () {
Router::get('/me', [MeController::class, 'show'])->name('me');
// → name 'api.me', URI '/api/v1/me'
});
});Domains
Router::domain(string|array $domains): DomainRouteGroup
Route by host. Supports a single domain or a list.
Router::domain('admin.example.com')->group(function () {
Router::get('/', [AdminDashboardController::class, 'index']);
});
Router::domain(['{tenant}.example.com'])->group(function () {
Router::get('/dashboard', [TenantDashboardController::class, 'index']);
}, ['middleware' => 'tenant']);The host's wildcard segments (e.g. {tenant}) are passed to the controller alongside path parameters.
Naming routes
RouteDefinition::name(string $name): self
Router::get('/users/{id}', [UserController::class, 'show'])->name('users.show');Build URLs with the global route() helper or Router::route():
route('users.show', ['id' => 42]); // '/users/42'
Router::route('users.show', ['id' => 42], absolute: true);Router::route(string $name, array $parameters = [], bool $absolute = true): string
$url = Router::route('users.show', ['id' => 42]); // 'http://example.test/users/42'Middleware
Middlewares are FQCNs; each must expose a handle(Request $request, Closure $next): mixed. A short-circuit (returning a Response) bypasses the next layer.
RouteDefinition::middleware(mixed $middlewares): self
Attach one or more.
Router::get('/dashboard', [HomeController::class, 'index'])
->middleware(\App\Middlewares\Auth::class);
Router::post('/users', [UserController::class, 'store'])
->middleware([\App\Middlewares\Auth::class, \App\Middlewares\VerifyCsrf::class]);Router::appendRouteMiddleware(string $method, string $path, mixed $middlewares): void
Internal helper used by groups; you generally don't call this directly.
Example middleware
namespace App\Middlewares;
use Closure;
use Zero\Lib\Auth\Auth;
use Zero\Lib\Http\Request;
use Zero\Lib\Http\Response;
class AuthMiddleware
{
public function handle(Request $request, Closure $next): mixed
{
if (! Auth::user()) {
return Response::redirect('/login');
}
return $next($request);
}
}Dispatch
Router::dispatch(string $requestUri, string $requestMethod): Response
Resolve a request to a Response. Called by public/index.php; you don't normally call it yourself.
$response = Router::dispatch($_SERVER['REQUEST_URI'], $_SERVER['REQUEST_METHOD']);
$response->send();Router::getRoutes(): array
Inspect the registered route table (useful for route:list-style tooling).
foreach (Router::getRoutes() as $method => $byPath) {
foreach ($byPath as $path => $route) {
echo "$method $path\n";
}
}Router::registerRouteName(string $method, string $path, string $name, string $prefix = ''): void
Internal API used by RouteDefinition::name() to wire names into the registry.
Putting it together
// routes/web.php
use App\Controllers\Auth\AuthController;
use App\Controllers\UserController;
use App\Middlewares\Auth as AuthMiddleware;
use Zero\Lib\Router;
Router::get('/login', [AuthController::class, 'showLoginForm'])->name('login');
Router::post('/login', [AuthController::class, 'login']);
Router::group(['middleware' => AuthMiddleware::class], function () {
Router::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Router::group(['prefix' => '/users', 'name' => 'users.'], function () {
Router::get('/', [UserController::class, 'index'])->name('index');
Router::get('/{id}', [UserController::class, 'show'])->name('show');
Router::put('/{id}', [UserController::class, 'update'])->name('update');
Router::delete('/{id}', [UserController::class, 'destroy'])->name('destroy');
});
});