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

Concept Core використовує League Route поверх PSR-7. Маршрути описуються у PHP-файлах; один спільний екземпляр Concept\Core\Http\Routing\Router (реалізує RouterInterface) обслуговує весь застосунок.

Контекст: життєвий цикл запиту, config/routes.php.

Підключення файлів маршрутів

У config/routes.php задають список файлів (routes.list) і опційно route interceptors (routes.interceptors). Підключає HttpServiceProvider при першому створенні RouterInterface:

return [
    'routes' => [
        'interceptors' => [
            // AclRouteAuthorization::class, // RouteInterceptorInterface
        ],
        'list' => [
            dirname(__DIR__) . '/routes/web.php',
        ],
    ],
];

Додаткові файли з компонентів підключаються під час ComponentsServiceProvider::boot() — компонент повертає шлях до routes.php через ComponentInterface::routes() Див. структура з компонентами.

У кожному файлі маршрутів доступна змінна $router (тип League\Route\Router / RouterInterface):

<?php declare(strict_types=1);

use League\Route\Router;

/** @var Router $router */

$router->get('/path', $handler);

Глобальні route interceptors (ACL, політики) реєструються в routes.interceptors — див. окремий розділ Route interceptors.

Обробники (handlers)

League Route підтримує кілька форм callable:

Контролер [Class, method]

Рекомендований варіант — метод контролера, який резолвиться з DI-контейнера:

use Concept\App\Controllers\IndexController;

$router->get('/', [IndexController::class, 'index'])->setName('home');

Екземпляр контролера та аргументи методу підставляє RouteStrategy (FormRequest, ServerRequestInterface, параметри URL, Caster). Детальніше — Контролери.

Closure

use Concept\Core\Http\Contracts\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;

$router->get('/health', function (ResponseFactoryInterface $response): ResponseInterface {
    return $response->json(['status' => 'ok']);
});

Для простих endpoint-ів; для складної логіки краще винести в контролер.

HTTP-методи

Метод RouterHTTP
get()GET
post()POST
put()PUT
patch()PATCH
delete()DELETE
map()кілька методів

Параметри URL

Сегменти в фігурних дужках потрапляють у атрибути запиту та в аргументи handler:

$router->get('/admin/users/edit/{id:number}', [UserController::class, 'edit']);

Суфікс :number — обмеження League Route (лише цифри). Інші: :alpha, :alphanum, :segment тощо. Без суфікса — довільний сегмент ({token}).

У контролері параметр з тією ж назвою та типом (через CasterInterface):

public function edit(int $id): ResponseInterface

Групи маршрутів

Спільний префікс URI та middleware для групи:

use League\Route\RouteGroup;
use Concept\Components\AuthAdmin\Middlewares\AuthMiddleware;

$router->group('/admin', function (RouteGroup $route) {
    $route->get('/', [AdminController::class, 'index'])->setName('admin.home');
    $route->get('/logout', [AdminController::class, 'logout'])->setName('admin.logout');
})->lazyMiddleware(AuthMiddleware::class);

URL /admin та /admin/logout проходять через AuthMiddleware. Вкладені групи підтримуються.

Middleware на маршрутах

Глобальний — для всіх маршрутів у файлі (зазвичай на початку routes/web.php). Мінімальний skeleton не підключає middleware

$router->lazyMiddleware(VerifyCsrfTokenMiddleware::class);
$router->lazyMiddleware(ShareViewDataMiddleware::class);

На групу або маршрут — ланцюжок доповнюється для збігів у цій області:

$router->post('/projects/store', [ProjectController::class, 'store'])
    ->setName('projects.store')
    ->lazyMiddleware(ActiveUserMiddleware::class);

lazyMiddleware — клас резолвиться з контейнера під час запиту. Детальніше — Middleware.

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

Кожному маршруту можна задати ім’я для генерації URL:

$router->get('/login', [CabinetController::class, 'showLogin'])->setName('cabinet.login');

У PHP через UrlGeneratorInterface:

// шлях від кореня: /admin/users/edit/5
$path = $urlGenerator->uri('admin.user.edit', ['id' => 5]);

// повний URL: https://example.com/admin/users/edit/5
$fullUrl = $urlGenerator->url('admin.user.edit', ['id' => 5]);

$origin = $urlGenerator->base();

Редірект за іменем — ResponseFactoryInterface::redirectByName():

return $this->response->redirectByName('admin.users');

У Twig — AppExtension у skeleton:

{{ uri('cabinet.login') }}
{{ url('admin.user.edit', { id: user.id }) }}
{{ base_url() }}

Маршрути компонентів

Компонент повертає абсолютний шлях до routes.php через ComponentInterface::routes() (типовий патерн — __DIR__ . '/routes.php'). Приклад: src/Components/AuthAdmin/routes.php з префіксами /admin, /admin/users.

Усі маршрути потрапляють в один Router — уникайте дублювання імен (setName) між файлами.

Перегляд маршрутів (CLI)

php bin/console.php route:list
php bin/console.php route:list --full-middleware
php bin/console.php route:list -F

Команда route:list виводить метод, URI, ім’я, handler і middleware. Потрібен ConsoleServiceProvider і реєстрація RouteListCommand у config/commands.php (у skeleton уже є).

Поради

  • Тримайте публічні маршрути в routes/web.php, модульні — у Component/routes.php.
  • Завжди іменуйте маршрути, які з’являються у шаблонах або редіректах.
  • POST/PUT/PATCH/DELETE з формами — разом із VerifyCsrfTokenMiddleware і CSRF-полем.
  • Захищені зони — group() + auth-middleware; ACL — route interceptors.