Документация/Маршрутизация

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

Маршрутизация — это первостепенная задача фреймворка по обработке входящего запроса. Здесь определяется маршрут, отвечающий за адрес запроса и назначаются дальнейшие действия.

Иногда маршрутизацию во фреймворках называют "роутингом", это одно и то же.

Маршруты проекта составляются разработчиком в файле /routes/map.php, в этот файл могут быть вставлены (инклюдированы) другие файлы с маршрутами из папки "routes", которые вместе и составят карту маршрутизации. Особенностью данных маршрутов стоит считать, что при их загрузке идёт проверка фреймворком на общую правильность и последовательность использованных методов, в случае исключения генерируется ошибка с указанием причины исключения. Так как в карте маршрутов проверке подвергаются все существующие там маршруты, это гарантирует их общую корректность.

После первого запроса или при использовании специальной консольной команды происходит обновление и кеширование маршрутов. Поэтому файлы с маршрутами не должны включать внешний код, только методы класса Route.

Если после проведенных изменений в карте маршрутизации фреймворк не сгенерировал характерных сообщений, то в дальнейшем эти сообщения не будут появляться, по крайней мере до следующих изменений в подключенных файлах маршрутов.

Маршруты определяются методами класса Route, часто используемым из которых является get(). Методы этого класса используются только в карте маршрутизации.


#Метод Route::get()

При помощи этого метода можно указать обработку HTTP-метода GET по указанным условиям. Как показано на примере:

Route::get('/''Hello, world!');

Маршрут выведет при обращении к корневому URL сайта строчку "Hello, world!". Чтобы вывести HTML-код (может содержать и PHP-код) из шаблона, метод применяется совместно с функцией view().


#Динамические адреса

Фреймворк HLEB2 обрабатывает произвольные адреса по заданной разработчиком приложения схеме, например:

Route::get('/resource/{version}/{page}/''Dynamic address used');

В указанном случае все адреса URL, отвечающие условной схеме "site.com/resource/.../.../" будут отдавать одинаковую текстовую строчку, а значения "version" и "page" становятся доступны из объекта Hleb\Static\Request: Request::param("version")->asString() и Request::param("page")->asPositiveInt().
Также эти значения можно получить из контейнера и через одноимённые аргументы метода контроллера.

В адресе маршрута допустимо указать, что последняя часть его может отсутствовать:

Route::get('/profile/user/{id?}/''Variable ID value');

Route::get('/contacts/form?/''Optional end part');

В случае отсутствия адрес все равно совпадёт с этим маршрутом, но значение 'id' будет равно NULL.


#Значения по умолчанию для динамических адресов

Пример динамического маршрута в котором указаны значения для second и third именованных частей.

Route::get('/example/{first}/{second:two}/{third:three?}''defaults value in dynamic route');

Аналогично '/example/{first}/two/three?', только в данных Request будут добавлены к уже имеющемуся динамическому параметру 'first' дополнительные значения ['second' => 'two', 'third' => 'three']. Если конечный параметр отсутствует, то будет равен null.


#Вариативные адреса

Множественное назначение маршрутов (в Request::param() окажется нумерованный массив с частями URL):

Route::get('/example/...0-5/''From 0 to 5 arbitrary parts');
// or
Route::get('/example/...1-3,7,9,11-20/''Number of parts within the specified range');

#Тег в адресе

Фреймворк не позволяет интерпретировать части URL как составные, так как это противоречит стандартам, но из этого правила есть исключение.
Распространена ситуация, когда логин пользователя предваряется специальным тегом @ в URL. Задать его можно так:

Route::get('/profile/@{username}''Username with tag');

#Функция view()

Функция указывает, какой шаблон из папки /resources/views/ соотнести с маршрутом. Пример для файла /resources/views/index.php:

Route::get('/'view('index'));

Вторым аргументом функции можно передать переменные в именованном массиве.

Route::get('/'view('index', ['title' => 'Index page']));

Переменные будут доступны в шаблоне.

<?php
// File /resources/views/index.php

/** @var string $title */
echo $title// Index page

Для предопределённых адресов '404', '403' и '401' в функции view() будет выведена соответствующая стандартная страница ошибки.


#Группировка маршрутов

Группировка маршрутов используется для назначения общих свойств маршрутам путем добавления методов к группам, после этого действие метода распространяется на всю группу.
Определение области действия группы обозначается методом toGroup() в начале группы и endGroup() по завершению.

Route::toGroup()->prefix('example');

    
// /example/first/page/
    
Route::get('first/page''First page content');
    
// /example/second/page/
    
Route::get('second/page''Second page content');

Route::endGroup();

В данном случае метод prefix(), добавленный к группе, распространяет своё действие на все маршруты в ней.

Группы могут быть вложены в другие группы. Также существует альтернативный синтаксис для групп:

Route::toGroup()
    ->
prefix('example')
    ->
group(function () {
        
// /example/first/page/
        
Route::get('first/page''First page content');
        
// /example/second/page/
        
Route::get('second/page''Second page content');
    });

#Именованные маршруты

Каждому маршруту можно назначить уникальное название.

Route::get('/'view('default'))->name('homepage');

По этому названию можно генерировать его URL и сделать код независимым от реальных адресов URL.
Это достигается использованием названий вместо адресов. Например, этот сайт оперирует именами маршрутов для составления ссылок на страницы.


#Обработка HTTP-методов

По аналогии с методом get() для HTTP-метода GET, существуют методы post(), put(), patch(), delete(), options() по соответствию с POST, PUT, PATCH, DELETE, OPTIONS.

Эти методы одинаково соответствуют своему HTTP-методу, кроме options().
Во всех остальных случаях метод OPTIONS обрабатывается по стандарту, только в options() можно задать обработку OPTIONS отдельно (переназначить).

Route::options('/ajax/query/''...')->controller(OptionsController::class);

Route::post('/ajax/query/''{"result": "ok"}')->name('post.example.query');

#Метод Route::any()

Назначенный маршруту, соответствует всем HTTP-методам, в остальном аналогичен get().


#Метод Route::match()

Аналогичен методу get(), только имеет дополнительный первый аргумент, в котором можно передать массив с поддерживаемыми HTTP-методами.

Route::match(['get''post'], '/''Handler for POST and GET methods');

#Метод Route::fallback()

Перехватывает все не сопоставленные пути для всех HTTP-методов (или для указанных). Может быть только один метод fallback() в маршрутах для конкретного HTTP-метода.

Таким образом можно назначить соответствие для не найденного совпадения (вместо ошибки 404) для всех типов HTTP-методов или в отдельности.


#Защита маршрутов

Для защиты маршрутов от атак типа CSRF предназначен метод protect(). При назначении его к маршруту или группе маршрутов добавляет проверку на наличие специального токена, установленного ранее.

Route::get'/ajax/query''Protected route')->protect();

Работает это следующим образом:
На странице выводится токен доступа, можно использовать функцию csrf_token() или csrf_field().
Этот токен передается при помощи JavaScript или в форме вместе с запросом.
Маршрут запроса имеет метод protect() и происходит проверка токена.


#Назначение контроллера

Контроллер — часть архитектуры MVC (Action-Domain-Responder для веб), отвечает за дальнейшее управление обработкой запроса, уже идентифицированного маршрутизатором, но не должен содержать бизнес-логику.

Контроллер не может быть использован для группы маршрутов, он назначается конкретному или нескольким по отдельности. Для этого используется метод controller().

use App\Controllers\DefaultController;

Route::get('/')->controller(DefaultController::class, 'index');

В примере первым аргументом указан класс назначаемого контроллера, вторым - используемый метод контроллера. Метод 'index' можно не указывать, он используется по умолчанию.
Можно заметить, что у метода get() отсутствует уже ненужный с контроллером второй аргумент.


#Контроллеры-посредники

Если контроллер можно назначить только один к маршруту, то посредников (middlewares) может быть несколько, также middleware можно назначить к группе маршрутов.

Route::toGroup()
    ->
middleware(FirstGeneralMiddleware::class)
    ->
middleware(SecondGeneralMiddleware::class);

    
Route::get('/example''...')->middleware(GetMiddleware::class);
    
Route::post('/example''...')->middleware(PostMiddleware::class);

Route::endGroup();

Метод middleware() означает, что посредник будет выполнен до основного обработчика маршрута. Этому методу есть аналогичный before() и метод after() (выполнится после основного обработчика). Под основным обработчиком здесь подразумевается возвращаемый текст из маршрута, назначенный шаблон или выполнение контроллера.

Назначенные посредники выполняются в той последовательности, в которой расположены.

Аргументы метода для посредника сходны с контроллером, вторым аргументом можно задать выполняемый метод, по умолчанию 'index'. Отличием является наличие третьего аргумента, в нём можно передать массив параметров в middleware. Указанные параметры доступны в методе Hleb\Static\Router::data() или через контейнер.


#Модули

Модуль — это разновидность контроллера, он указывает в папку /modules/ проекта и содержит название используемого модуля.

Route::get('/section/')->module('default'DefaultModuleController::class);

#Проверка where()

Маршрут может содержать динамические части в адресе и методом where() можно установить правила для этих частей.

Route::toGroup()
    ->
prefix('/{lang}/')
    ->
where(['lang' => '[a-z]{2}']);

    
Route::post('/profile/{user}/{id}''...')
        ->
where(['user' => '/[a-z]+/i''id' => '[0-9]+']);

Route::endGroup();

В этом примере части под названием 'lang', 'user' и 'id' будут проверены с помощью регулярных выражений.


#Ограничение по доменам

Специальный метод domain() может быть назначен маршруту или группе маршрутов.
Первым аргументом можно указать название домена или поддомена, вторым аргументом уровень соответствия этого правила.


#Принцип подстановки

Существует способ, при котором целевой контроллер и метод определяются исходя из значений динамического адреса.
Маршрут в этом случае может быть таким:

Route::get('/page/{controller}/{method}')
    ->
controller('Main<controller>Controller''init<method>');

В этом примере для URL /page/part/first/ фреймворк попробует определить контроллер как 'MainPartController' и метод 'initFirst'(преобразовав по принципу camelCase).

Принцип подстановки в обработчики должен быть контролируемым, так как в URL могут попасть данные, которые вызовут непредусмотренный заранее контроллер или его метод.

Кроме этого, можно дополнительно указать зависимость от HTTP-метода запроса ключом '[verb]'.

Route::match(['get''post'], '/page/{target}')
    ->
controller('Main<target>[verb]Controller''[verb]Method>');

В этом примере для URL /page/example/ фреймворк попробует определить контроллер как 'MainExampleGetController' и метод 'getMethod'(преобразовав по принципу camelCase).
Для метода POST это будут 'MainExamplePostController' и 'postMethod'.

Возможность подстановки может быть особенно полезной при распределении HTTP-методов запроса по методам контроллера.


#Обновление кеша маршрутов

По умолчанию во фреймворке кеш маршрутов обновляется автоматически после внесения изменений в файл /routes/map.php. Существует также консольная команда для обновления кеша маршрутов:

$php console --routes-upd

Для проекта с большой посещаемостью возможно возникнет надобность отключить в production автоматическое обновление и выполнять перерасчёт кеша маршрутов только консольной командой.
Это делается через настройку 'routes.auto-update' в файле /config/common.php.

Использование хостинга Контроллер

Страница создана: @fomiash
К началу страницы