Роутинг для PSR 7 Response/Request. Часть 2: Проектируем Router
Содержание:
- Часть 1. Введение и простой роутинг
- Часть 2. Система маршрутизации — продумываем Router (вы тут)
- Часть 3. Создание кода роутинга (Скоро)
Добавление правил
Придумываем объект маршрутизации и его метод для добавления правила маршрутизации, в который передаем наш путь, HTTP метод и анонимную функцию:
Поиск нужного правила
Чтобы понять, какой обработчик к нашему запросу относится — придумываем метод, в который мы передаем наш Request, а он сам будет производить проверку по правилам выше и возвращать результат, из которого мы сможем извлекать экшн (анонимная функция) и атрибуты — далее мы должны найденные атрибутты в правилах передать в экшн, точнее в объект Request.
По итогу это должно выглядеть так:
Дополнительные методы для добавления правил**
Прежде чем создавать код для объекта Router — думаем над тем, что можно упростить.
Для того, чтобы не указывать HTTP методы в параметрах, мы можем создать методы объекта Router с названием этих HTTP методов:
Это более удобно, не нужно заморачиваться, также будет работать автоподстановка.
Проблема — много методов
В итоге мы придумали кучу методов, и по итогу можно запутаться — какие методы относятся к масиву правил, а что относится к нему.
Для решения этого — роутер разделяем на 2 части: на коллекции роутов и сам роутер.
https://gist.github.com/Maksclub/e09b1f4ad82b53b813ceb8a3043a878b
Помимо упрощения использования — данное разделение позволяет его использовать надежнее, например потом в контроллере, используя объект Router, нельзя будет добавить маршрут, так как у него нет таких методов.
Генерация адресов
Помимо разбора адресов при разработке и поддержке проектов необходимо адреса генерировать.
Конечно в самих представлениях мы можем выводить жестко:
<a href="/blog/<?= $post->id?>">
<?= htmlspecialchars($post->title)?>
</a>
Но если нам нужно поменять роуты с /blog/ на /post/ или в магазине /product/ на еще какой-нибудь, то придется в ручную менять во всех местах, что может вылезти косяками в работе проекта.
Тогда придумываем метод generate() для нашего объект Router, с помощью которого мы и будем выводить адреса по названию маршрута:
<a href="<?= $router->generate('название_маршрута', ['id' => $post->id]) ?>">
<?= htmlspecialchars($post->title)?>
</a>
Как видите нам необходимо имя маршрута. В принципе мы можем использовать путь, который указывали в правилах, но в случае регулярки при ее изменении нам придется менять ее во многих местах, потому для правил добавим еще один параметр с названием роута.
Укрощаем регулярное выражение в правилах
Громоздкие регулярные выражения не удобно записывать, а также метод генерации адреса с регуляркой не работает.
Тогда вместо:
$router->get('blog_show', '/blog/(?P<id>\d+)', ...);
Можно будет упрощенно использовать так:
$router->get('blog_show', '/blog/{id:\d+}', ...);
Но тогда мы не сможем использовать в данном случае сложные правила, например указать число символов в регулярном выражении, так как фигурные скобки нам усложнят парсинг адресов.
Как мы можем поступить:
- Сам параметр указать чистым, а регулярное выражение передать отдельным параметром, например:
$router->get('blog_show', '/blog/{id}', ..., ['id' => '\d+']);
- Сделать систему с необязательными параметрами, а сами параметры передавать как токены и задавать значения по умолчанию:
$router->get('blog_show', '/blog[/{page}]', ...,
[
'tokens' => ['page' => '\d+'],
'default' => ['page' => 1],
]);
Будет делать именно второй вариант.