Routing
Basic Routing
Routes are defined in routes/web.php using the static Route facade. The router supports all standard HTTP verbs.
<?php
use Lyger\Routing\Route;
use Lyger\Http\Request;
use Lyger\Http\Response;
Route::get('/path', $handler);
Route::post('/path', $handler);
Route::put('/path', $handler);
Route::delete('/path', $handler);
Route Handlers
Closure Handler
Pass an anonymous function directly:
Route::get('/', function () {
return Response::json(['status' => 'ok']);
});
The Request object is always available as the first parameter when you need it:
Route::get('/search', function (Request $request) {
$query = $request->get('q', '');
return Response::json(['query' => $query]);
});
Controller Handler
Pass [ControllerClass::class, 'methodName']:
use App\Controllers\UserController;
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
Route::get('/users/{id}', [UserController::class, 'show']);
Route::put('/users/{id}', [UserController::class, 'update']);
Route::delete('/users/{id}', [UserController::class, 'destroy']);
The container automatically resolves the controller and injects its dependencies.
Route Parameters
Use {paramName} syntax to capture URI segments:
Route::get('/users/{id}', function (Request $request, $id) {
return Response::json(['user_id' => $id]);
});
Route::get('/posts/{postId}/comments/{commentId}', function (Request $request, $postId, $commentId) {
return Response::json([
'post' => $postId,
'comment' => $commentId,
]);
});
Parameter injection order: Request is always first, then route parameters in the order they appear in the URI pattern.
In a controller method:
class PostController
{
public function show(Request $request, int $id): Response
{
$post = Post::find($id);
if (!$post) {
return Response::error('Post not found', 404);
}
return Response::json($post->toArray());
}
}
Response Types
Any value returned from a route handler is automatically coerced to a Response:
| Returned Value | Behavior |
|---|---|
Response object | Sent as-is |
array | Converted to JSON response |
string | Sent as plain text |
null | Empty 200 response |
// All of these work:
Route::get('/a', fn() => Response::json(['x' => 1]));
Route::get('/b', fn() => ['x' => 1]); // Auto JSON
Route::get('/c', fn() => 'Hello World'); // Auto text
Controller Class Structure
A typical controller:
<?php
namespace App\Controllers;
use Lyger\Http\Request;
use Lyger\Http\Response;
use App\Models\User;
class UserController
{
public function index(Request $request): Response
{
$users = User::all();
return Response::json($users->toArray());
}
public function show(Request $request, int $id): Response
{
$user = User::findOrFail($id);
return Response::json($user->toArray());
}
public function store(Request $request): Response
{
$user = User::create($request->all());
return Response::json($user->toArray(), 201);
}
public function update(Request $request, int $id): Response
{
$user = User::findOrFail($id);
$user->fill($request->all());
$user->save();
return Response::json($user->toArray());
}
public function destroy(Request $request, int $id): Response
{
$user = User::findOrFail($id);
$user->delete();
return Response::json(['deleted' => true]);
}
}
Route File Loading
The router loads routes from routes/web.php via the bootstrapper in public/index.php:
$router->loadRoutesFromFile(__DIR__ . '/../routes/web.php');
You can split your routes into multiple files by requiring them inside web.php:
// routes/web.php
require __DIR__ . '/api.php';
require __DIR__ . '/admin.php';
Route Registration Internals
Routes are stored in the static Route class and then imported by Router:
// Route facade (static collection)
Route::get('/users', [UserController::class, 'index']);
// Router processes them on dispatch:
$router->dispatch($request);
// → matches URI pattern
// → resolves controller from container
// → injects Request + route params
// → returns Response
Pattern Matching
The router uses a regex-free custom pattern matcher. The {param} segments are extracted and matched positionally against URI segments:
Pattern: /users/{id}/posts/{postId}
URI: /users/42/posts/7
Result: ['id' => '42', 'postId' => '7']
Trailing slashes are normalized automatically.
Accessing All Defined Routes
You can inspect all registered routes programmatically:
$routes = Route::getRoutes();
// Returns: [['method' => 'GET', 'path' => '/users', 'handler' => ...], ...]
To clear all routes (useful in testing):
Route::clear();