Skip to content

Routing and Controllers

Precious Lyson edited this page Nov 11, 2026 · 1 revision

Routing and Controllers

HTTP

Machinjiri's Router maps HTTP method and path pairs to closures or controller handlers. Route definitions belong in your application, usually in its generated route files.

Basic routes

use Mlangeni\Machinjiri\Core\Routing\Router;

Router::get('/', [HomeController::class, 'index'], 'home');
Router::post('/contact', [ContactController::class, 'store'], 'contact.store');
Router::put('/profile', [ProfileController::class, 'update'], 'profile.update');
Router::delete('/profile', [ProfileController::class, 'destroy'], 'profile.destroy');

The framework also exposes Router::any() for common HTTP methods, Router::ajax() for AJAX-only methods, and Router::traditional() for non-AJAX GET/POST handling. Prefer explicit methods for normal application endpoints.

Closures and controller handlers

A closure can be useful for a tiny route:

Router::get('/health', function () {
    return 'ok';
}, 'health');

For application logic, use a controller:

Router::get('/about', [PageController::class, 'about'], 'about');

Controllers can extend Mlangeni\Machinjiri\Core\Artisans\Base\AbstractController. The framework's route handler resolver handles controller invocation; follow the signatures used by the app's generated controllers. Request and response classes are in Mlangeni\Machinjiri\Core\Http.

Path parameters

Named braces define route parameters:

Router::get('/hello/{name}', [HomeController::class, 'greeting'], 'greeting');

The route handler receives the parameter as part of its invocation. A corresponding controller method may accept it after the request and response parameters:

public function greeting(
    HttpRequest $request,
    HttpResponse $response,
    string $name
): string {
    return $this->view('greeting', ['name' => $name]);
}

Use route constraints when a parameter must match a particular pattern:

Router::get('/users/{id}', [UserController::class, 'show'], 'users.show')
    ->where('id', '[0-9]+');

Constraints are regular-expression fragments. Keep them narrow and test the expected matching behavior.

Route groups and middleware

Groups can apply a path prefix and middleware to their routes:

Router::group([
    'prefix' => '/admin',
    'middleware' => [Authenticate::class],
], function () {
    Router::get('/dashboard', [AdminController::class, 'index'], 'admin.dashboard');
    Router::get('/reports', [ReportController::class, 'index'], 'admin.reports');
});

When using middleware, make sure the middleware is registered/resolvable in the generated application's runtime and that it follows the framework's middleware contract. Route-specific options can also include middleware:

Router::get('/account', [AccountController::class, 'show'], 'account', [
    'middleware' => [Authenticate::class],
]);

Resource routes

Router::resource($path, $controller) registers the conventional resource actions (index, create, store, show, edit, update, and destroy):

Router::resource('/posts', PostController::class);

Limit generated actions with only or exclude actions with except:

Router::resource('/posts', PostController::class, [
    'only' => ['index', 'show'],
]);

Check the generated controller's method names and route conventions before relying on resource routes in an existing app.

Named routes and fallback handling

Use a route name when registering it, then generate a URL with the router:

$url = Router::route('users.show', ['id' => 42]);
$absoluteUrl = Router::absoluteRoute('users.show', ['id' => 42]);

For a custom not-found handler:

Router::fallback(function () {
    return 'Page not found';
});

The framework otherwise sends a 404 response when no route matches.

CORS and rate limits

Route options support CORS and a named rate-limit policy:

Router::post('/api/items', [ItemController::class, 'store'], 'api.items.store', [
    'rate_limit' => 'api',
    'cors' => [
        'allowed_origins' => ['https://app.example.com'],
        'allowed_methods' => ['POST', 'OPTIONS'],
        'allowed_headers' => ['Content-Type', 'Authorization'],
    ],
]);

Define rate-limit policies in the routing configuration used by your app. Do not use wildcard origins or broad methods in production unless they are explicitly appropriate for the API.

Request lifecycle

At a high level:

  1. The application bootstraps framework services and loads route definitions.
  2. Router::dispatch() obtains the request and matches its method and path.
  3. Route bindings, rate limiting, and middleware are applied where configured.
  4. The handler executes and the result is returned/sent by the application runtime.

Register routes before dispatch. Avoid putting slow business logic, database migrations, or one-time setup work in the route declaration file.

More

Clone this wiki locally