Core

Routing

All HTTP routes are defined in a single file — routes/web.php. Each route maps one URI pattern to one action class.

Defining routes

<?php

use App\Actions\HomeAction;
use App\Actions\AboutAction;
use System\Routing\Route;

Route::get("/", HomeAction::class)->name("home");
Route::get("/about/{id}/get/{text}", AboutAction::class)->name("about");

Supported verbs

Route::get($uri, $action);
Route::post($uri, $action);
Route::put($uri, $action);
Route::patch($uri, $action);
Route::delete($uri, $action);

Each verb is a static factory over a shared Route::add($method, $uri, $handler) registration.

Rules & behavior

  • The $action must be the fully-qualified class name of an action class.
  • If the class doesn’t exist, an AppException is thrown at registration time — fail fast.
  • {param} segments are compiled to regex ([^/]+) captures; the matched values (URL-decoded) are passed to main().
  • Matching is first-match-wins within the same HTTP verb. No match → NotFoundException.
  • Duplicate route names throw an AppException.
  • Routes are static and global — register them at the top level of routes/web.php.
Fail fast

Because action classes are validated during registration, a typo in a class name blows up as soon as routes load — not on the first request that happens to hit it.

Route parameters

Placeholders in the URI become an array passed to main($params):

// routes/web.php
Route::get("/about/{id}/get/{text}", AboutAction::class)->name("about");

// app/Actions/AboutAction.php
public function main(array $params) {
    $params["id"];   // "45"
    $params["text"]; // "hello"
}

The current route’s parameters are also available anywhere via Route::params():

Route::params(); // [ "id" => 45, "text" => "hello" ]

Named routes

Give a route a name, then generate URLs from it instead of hard-coding paths:

Route::get("/about/{id}/get/{text}", AboutAction::class)->name("about");

// Generate a URL
route("about", ["id" => 45, "text" => "hello"]); // "/about/45/get/hello"
Provide every parameter

Unmatched {placeholders} are left as-is in generated URLs — always pass every param the route declares.

Current route helpers

route();                   // "home", "about", ... or null
route_is("home");          // true if the current route is "home"
route_in(["home", "about"]);// true if current route is in the list
url();                     // "http://example.com/current/path"

Redirects and status codes

Redirecting between named routes is a common pattern in actions:

return response()->redirect(route("products.index"));
return response()->redirect("/login", 301);   // permanent
return response()->back();                    // HTTP_REFERER, or "/"

See Actions & Responses for the full response API.

How dispatch works

Route::dispatch()
  ├─ match REQUEST_METHOD + REQUEST_URI against registered routes
  │   (compiled regex, first-match-wins)
  └─ Route::executeHandler()
      ├─ new Action
      ├─ boot()            (optional setup)
      ├─ middlewares()      → MiddlewarePipeline (onion)
      └─ main($params)      → result is echoed

Route is a final class holding static state (routes, named routes, current route). That’s a non-issue under standard Apache/PHP-FPM per-request processes.

Part of the Lunaris documentation · Source on Codeberg · MIT License