Маршрутизація

Ядро використовує League Route — не власний router. Навіщо: зрілий match / groups / middleware, PSR-7/15, без дублювання в Concept. Файли маршрутів підключає HttpKernelServiceProvider через require; у файлі доступна змінна $router (League\Route\Router).

HTTP surface тримаємо окремими файлами, а не в Bootstrap: URL → handler і списки middleware видно без читання контейнера. Інакше «що відбувається на /contact» довелося б збирати з кількох місць.

Підключення файлів

Шляхи — абсолютні й явні. Ядро не шукає routes/ за конвенцією: хто збирає додаток, той і передає список. Якщо файлу немає — одразу InvalidArgumentException, а не «тихий» порожній Router.

new HttpKernelServiceProvider(
    routePaths: [
        __DIR__ . '/../routes/web.php',
        __DIR__ . '/../routes/api.php',
    ],
    resolvers: [...],
);

Файл маршрутів

Мінімум: HTTP-метод, path, handler. Ім’я через setName — коли знадобиться interceptors, URL generator (у Http extension) або посилання з коду без хардкоду path.

<?php declare(strict_types=1);

use App\PingHandler;
use League\Route\Router;

/** @var Router $router */

$router->get('/ping', [PingHandler::class, 'ping'])->setName('ping');
$router->get('/hello/{name}', [PingHandler::class, 'hello']);

Handlers

League Route резолвить class-string з контейнера — тому constructor injection працює без «new» у route file. Три форми:

  • [Class::class, 'method'] — handler з DI
  • Closure — коли логіка справді одноразова
  • Invokable object — __invoke

Handler повинен повертати ResponseInterface. Інакше RouteStrategy кине RuntimeException — краще fail рано, ніж «щось» у body без Content-Type.

Параметри URL

Плейсхолдери {name} стають ключами в $route->getVars() і attributes enriched request. Ім’я плейсхолдера має збігатися з іменем параметра handler-а — саме так RouteParameterArgumentResolver знаходить значення без анотацій. Коли потрібен int/UUID з casting — це вже extension-resolver у chain, не зміна router-а.

Групи та middleware

Група — коли спільний префікс і/або той самий middleware-ланцюжок (API auth, HTML CSRF). Не вішайте HTML-middleware на JSON-групу «про запас»: поведінка запиту має бути очевидною з route file. Middleware — PSR-15, див. Middleware.

use League\Route\RouteGroup;

$router->group('/api', function(RouteGroup $router): void {
    $router->get('/status', [StatusHandler::class, 'show']);
})->lazyMiddlewares([AuthMiddleware::class]);

Іменовані маршрути

->setName('ping') — ім’я доступне через $route->getName() у interceptors і власній логіці. Коли: ACL за іменем, генерація URL, тести. Коли ні: одноразові ping-маршрути без посилань — ім’я можна не ставити.