Построение маршрутизации для товаров: от простого парсинга до FastRoute
Разработка маршрутизации для страниц товара с использованием полного пути через index.php является одной из ключевых задач при построении веб-приложений. Правильная реализация позволяет создавать чистые URL, упрощает поддержку кода и улучшает SEO. В данной статье рассматриваются различные подходы к организации маршрута типа /product/123 или /product/some-slug с помощью PHP и центрального точки входа.
Основные варианты реализации маршрута товара
Как организовать маршрутизацию товаров с минимальным объемом кода и высокой производительностью?
Наиболее эффективным решением является использование готовой библиотеки FastRoute (разработчик Nikita Popov). Она обеспечивает быстрый разбор URL, поддержку параметров и кеширование маршрутов. Пример реализации:
// index.php - точка входа
require_once 'vendor/autoload.php';
use FastRoute\RouteCollector;
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) {
$r->addRoute('GET', '/product/{id:\d+}', 'ProductController::show');
$r->addRoute('GET', '/product/{slug:[a-z0-9-]+}', 'ProductController::showBySlug');
});
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
// вызов контроллера с параметрами
list($class, $method) = explode('::', $handler);
(new $class)->$method($vars);
break;
case FastRoute\Dispatcher::NOT_FOUND:
http_response_code(404);
echo 'Товар не найден';
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
http_response_code(405);
echo 'Метод не разрешен';
break;
}Пояснение шагов:
- Подключается автозагрузка Composer.
- Создается диспетчер маршрутов с указанием правил. Используются именованные параметры с регулярными выражениями (например,
\d+для цифр). - Извлекаются метод и URI из суперглобальных массивов.
- Метод
dispatchвозвращает статус и данные маршрута. - В зависимости от статуса вызывается соответствующий обработчик или возвращается ошибка.
Распространенные проблемы:
- Некорректная работа .htaccess – без перенаправления всех запросов на index.php маршруты не будут обрабатываться. Решение: добавить в корневой .htaccess строки
RewriteEngine OnиRewriteCond %{REQUEST_FILENAME} !-fсRewriteRule ^(.*)$ index.php [QSA,L]. - Порядок маршрутов – если указать общий маршрут перед конкретным, он перекроет другие. Решение: размещать более специфичные маршруты раньше.
- Кеширование маршрутов – при отсутствии кеша производительность снижается. FastRoute поддерживает кеширование через передачу файла кеша в
simpleDispatcher().
Как реализовать маршрут товара без внешних библиотек, используя только нативный PHP?
Можно использовать простой парсинг URI с помощью explode и условных конструкций. Этот вариант полезен для небольших проектов или обучения.
// index.php без библиотек
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = explode('/', trim($uri, '/'));
if ($segments[0] === 'product' && isset($segments[1])) {
$productParam = $segments[1];
if (is_numeric($productParam)) {
// загрузка товара по ID
$product = getProductById((int)$productParam);
} else {
// загрузка товара по slug
$product = getProductBySlug($productParam);
}
if (!$product) {
http_response_code(404);
echo 'Товар не найден';
} else {
// отображение товара
renderProduct($product);
}
} else {
// обработка других страниц
}Как добавить поддержку нескольких языков или префиксов в URL товара?
Для этого можно использовать группировку маршрутов или параметры. В FastRoute это делается через вложенные маршруты или приставку /{lang:[a-z]{2}} перед путем. Пример с префиксом:
$r->addRoute('GET', '/{lang:en|ru}/product/{id:\d+}', 'ProductController::showLang');Расширенные примеры и результаты их работы
Как обработать маршрут товара с числовым идентификатором и текстовым слагом одновременно?
// Использование FastRoute с двумя маршрутами и контроллером
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) {
$r->addRoute('GET', '/product/{id:\d+}', 'showProductById');
$r->addRoute('GET', '/product/{slug:[a-zA-Z0-9_-]+}', 'showProductBySlug');
});
function showProductById($vars) {
echo "Загружен товар с ID: " . $vars['id'];
}
function showProductBySlug($vars) {
echo "Загружен товар со слагом: " . $vars['slug'];
}// Результат при запросе GET /product/42 Загружен товар с ID: 42 // Результат при запросе GET /product/noviy-smartfon Загружен товар со слагом: noviy-smartfon
Как добавить проверку существования товара и возвращать кастомные страницы 404?
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) {
$r->addRoute('GET', '/product/{id:\d+}', function($vars) {
$product = getProductById($vars['id']);
if (!$product) {
http_response_code(404);
echo "Товар не найден
";
return;
}
// рендеринг шаблона
echo "{$product['name']}
";
});
});
// Функция поиска товара
function getProductById($id) {
$products = [
1 => ['name' => 'Смартфон'],
2 => ['name' => 'Ноутбук']
];
return $products[$id] ?? null;
}// Запрос GET /product/1Смартфон
// Запрос GET /product/999Товар не найден
(с кодом 404)
Как сгруппировать все маршруты для товаров в одном контроллере с помощью префикса?
use FastRoute\RouteCollector;
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) {
$r->addGroup('/product', function (RouteCollector $r) {
$r->addRoute('GET', '', 'CatalogController::index');
$r->addRoute('GET', '/{id:\d+}', 'ProductController::show');
$r->addRoute('GET', '/{slug:[a-z0-9-]+}', 'ProductController::showBySlug');
});
});// Результат: URL /product вызывает каталог, /product/5 вызывает конкретный товар
Как применить middleware (например, авторизация) перед вызовом контроллера товара?
$container = new Container(); // PSR-11 контейнер
$dispatcher = FastRoute\simpleDispatcher(function(RouteCollector $r) use ($container) {
$r->addRoute('GET', '/product/{id:\d+}', function($vars) use ($container) {
$auth = $container->get('auth');
if (!$auth->check()) {
http_response_code(403);
echo 'Доступ запрещен';
return;
}
// вызов контроллера
$controller = $container->get('productController');
$controller->show($vars['id']);
});
});Для более гибкой реализации middleware рекомендуется использовать готовые PSR-15 библиотеки (например, Relay или Middlewares).