Роутинг для PSR 7 Response/Request. Часть 2: Проектируем Router

Содержание:

Добавление правил

Придумываем объект маршрутизации и его метод для добавления правила маршрутизации, в который передаем наш путь, 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],
]);

Будет делать именно второй вариант.

Популярное