Routing
Define the HTTP method, path and controller action in the route configuration. Named routes let you generate URLs with their parameters.
Registering routes
The runtime passes Router to the callback returned by Routing.php. getNamed() registers GET /articles/{id} as articles.detail, and ControllerAction::for() targets detail(). The {id} segment is passed to the action argument of the same name; the route name is used later to build a link.
<?php
declare(strict_types=1);
use App\Controllers\ArticleController;
use Lemonade\Framework\Routing\ControllerAction;
use Lemonade\Framework\Routing\Router;
return static function (Router $router): void {
$router->getNamed(
'articles.detail',
'/articles/{id}',
ControllerAction::for(ArticleController::class, 'detail'),
);
};Methods and parameters
Router exposes get(), post(), put(), patch(), and delete() for the corresponding HTTP methods. ArticleController receives {id} from the GET route as the detail() argument. Its store() action handles the POST route in the next file and returns JSON with HTTP 201; this example does not persist data.
<?php
declare(strict_types=1);
namespace App\Controllers;
use Lemonade\Framework\Http\Response\Responses;
use Lemonade\Framework\View\ViewRendererInterface;
use Psr\Http\Message\ResponseInterface;
final readonly class ArticleController
{
public function __construct(
private ViewRendererInterface $views,
private Responses $responses,
) {}
public function detail(string $id): ResponseInterface
{
return $this->views->render('articles.detail', ['id' => $id]);
}
public function store(): ResponseInterface
{
return $this->responses->json(['status' => 'created'], 201);
}
}The same Routing.php callback adds POST /articles for store() and a separate GET /articles/featured/{id} path. constrainParameter() accepts a list of allowed values, here 1 and 2, rather than a regular expression.
$router->postNamed(
'articles.store',
'/articles',
ControllerAction::for(ArticleController::class, 'store'),
);
$router->get('/articles/featured/{id}', ControllerAction::for(ArticleController::class, 'detail'))
->constrainParameter('id', ['1', '2']);Generating links
ArticleLinks receives UrlGenerator through its constructor and builds the link from articles.detail. The id fills the {id} segment; extra parameters would become a query string. Changing the route path then leaves callers untouched.
<?php
declare(strict_types=1);
use Lemonade\Framework\Routing\UrlGenerator;
final readonly class ArticleLinks
{
public function __construct(private UrlGenerator $urls) {}
public function detail(int $id): string
{
return $this->urls->route('articles.detail', ['id' => $id]);
}
}