Создание обработчика поиска товаров с помощью маршрутизации PHP
Организация маршрута для поиска товаров
Задача: построить единую точку входа (index.php), которая обрабатывает запросы поиска товаров по ключевому слову, категории или цене, используя ЧПУ-маршруты. Ниже представлено эффективное решение на базе FastRoute, а также альтернативные подходы.
Как организовать поиск товаров с минимальными накладными расходами и поддержкой параметров запроса?
Использование библиотеки FastRoute позволяет быстро определить маршруты и извлечь параметры. Установка через Composer: composer require nikic/fast-route.
Пример базового index.php:
<?php
require_once '../vendor/autoload.php';
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/search/{keyword}', 'search_by_keyword');
$r->addRoute('GET', '/search/category/{category}', 'search_by_category');
$r->addRoute('GET', '/search', 'search_with_filters');
});
$httpMethod = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
// передаём управление соответствующей функции
call_user_func_array($handler, $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;
}
function search_by_keyword($keyword) {
// экранирование и поиск
$safeKeyword = htmlspecialchars($keyword, ENT_QUOTES, 'UTF-8');
echo "Результаты поиска по слову: $safeKeyword";
}
function search_by_category($category) {
echo "Товары в категории: $category";
}
function search_with_filters() {
// чтение $_GET параметров
$priceMin = $_GET['price_min'] ?? 0;
$priceMax = $_GET['price_max'] ?? 999999;
echo "Поиск по цене от $priceMin до $priceMax";
}
Route info php id (информация о маршруте по id php)
Пояснения:
- Маршрут
/search/{keyword}захватывает динамический сегмент. - Маршрут
/searchбез параметров пути использует$_GETдля фильтров. - Обработка ошибок 404 и 405 встроена.
Возможные проблемы:
- Кеширование маршрутов – FastRoute по умолчанию кеширует, но для этого нужно настроить write-директорию. Если кеш не обновляется, из-за добавления новых маршрутов можно получить 404. Решение: удалить файл кеша или использовать режим без кеша на время разработки.
- Кириллические URL – параметры могут содержать UTF-8 символы. FastRoute их корректно обрабатывает, но нужно декодировать (например,
urldecode). - Пустой ключ – если
{keyword}отсутствует, маршрут не сработает. Лучше сделать дополнительный маршрут/search/или обрабатывать пустоту в контроллере.
Как реализовать маршрутизацию без внешних зависимостей, используя только PHP?
Простейший switch/case по значению $_GET['route'] или $_SERVER['REQUEST_URI']. Минус – нет гибкости с параметрами пути.
<?php
$uri = trim($_SERVER['REQUEST_URI'], '/');
$parts = explode('/', $uri);
if ($parts[0] === 'search') {
if (isset($parts[1])) {
$keyword = $parts[1];
echo "Поиск по: $keyword";
} else {
// фильтры из ?price_min=…
$priceMin = $_GET['price_min'] ?? 0;
echo "Фильтр по цене: $priceMin";
}
} else {
http_response_code(404);
echo 'Страница не найдена';
}
Ru index php route (маршрут index.php с русским языком)
Проблемы:
- Жёсткая структура, сложно добавлять новые сегменты.
- Нет различения HTTP методов.
- Легко допустить ошибки с индексами массива.
Как задать гибкий шаблон маршрута с помощью регулярных выражений?
Использование preg_match позволяет извлекать именованные группы. Подходит для небольших проектов.
<?php
$uri = $_SERVER['REQUEST_URI'];
$pattern = '#^/search/(?P<keyword>[^/]+)$#';
if (preg_match($pattern, $uri, $matches)) {
$keyword = $matches['keyword'];
echo "Ищем товары: $keyword";
} else {
http_response_code(404);
}
Admin index php route (маршрут админки php)
Сложность поддержки регулярных выражений, особенно при большом количестве маршрутов. Ошибки в паттерне ведут к незаметным багам.
Как применить атрибуты (PHP 8) для автоматической маршрутизации поиска товаров?
Метод с рефлексией: атрибут #[Route] привязывает метод класса к маршруту.
<?php
#[Attribute]
class Route {
public function __construct(public string $path) {}
}
class ProductController {
#[Route('/search/{keyword}')]
public function search($keyword) {
echo "Поиск по $keyword";
}
}
// Загрузчик (сокращённо)
$uri = $_SERVER['REQUEST_URI'];
$ref = new ReflectionClass(ProductController::class);
foreach ($ref->getMethods() as $method) {
$attr = $method->getAttributes(Route::class);
if ($attr) {
$route = $attr[0]->newInstance();
$pattern = preg_replace('/\{[^}]+\}/', '([^/]+)', $route->path);
if (preg_match("#^$pattern$#", $uri, $params)) {
array_shift($params);
$method->invoke(new ProductController(), ...$params);
}
}
}
Index php route information (маршрут информационной страницы php)
Требует аккуратной реализации загрузчика. Неэффективно при каждом запросе сканировать классы. Рекомендуется кешировать список маршрутов.
Как использовать готовый компонент Symfony Routing для поиска товаров?
Установка: composer require symfony/routing. Компонент поддерживает сложные требования и проверки.
<?php
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
$routes = new RouteCollection();
$routes->add('search_keyword', new Route('/search/{keyword}', [
'_controller' => 'search_action',
]));
$routes->add('search_filters', new Route('/search', [
'_controller' => 'filter_action',
]));
$context = new RequestContext();
$context->fromRequest();
$matcher = new UrlMatcher($routes, $context);
try {
$parameters = $matcher->match($context->getPathInfo());
$handler = $parameters['_controller'];
// вызов соответствующей функции
if ($handler === 'search_action') {
echo 'Поиск по: ' . $parameters['keyword'];
}
} catch (\Symfony\Component\Routing\Exception\ResourceNotFoundException $e) {
http_response_code(404);
}
Избыточность для простого поиска, но удобно в больших проектах. Может снижать производительность из-за дополнительных абстракций.
Дополнительные примеры и расширенные сценарии
Ниже приведены более глубокие варианты использования маршрута поиска товаров.
1. Маршрут с пагинацией и несколькими параметрами
Пример на FastRoute с комбинированными сегментами и GET-параметрами.
$r->addRoute('GET', '/search/{category}/page/{page:\d+}', 'search_paginated');
function search_paginated($category, $page) {
$sort = $_GET['sort'] ?? 'name';
$itemsPerPage = 10;
$offset = ($page - 1) * $itemsPerPage;
echo "Категория: $category, страница $page, сортировка: $sort, смещение: $offset";
}
При запросе /search/electronics/page/2?sort=price выведет: Категория: electronics, страница 2, сортировка: price, смещение: 10
2. Обработка JSON-запросов для API поиска
Маршрут принимает POST с телом JSON и возвращает результаты в формате JSON. FastRoute с POST-методом.
$r->addRoute('POST', '/api/search', 'api_search');
function api_search() {
$input = json_decode(file_get_contents('php://input'), true);
$keyword = $input['keyword'] ?? '';
$minPrice = $input['price_min'] ?? 0;
// имитация поиска
$results = ['item1', 'item2'];
header('Content-Type: application/json');
echo json_encode(['status' => 'ok', 'data' => $results]);
}
Ответ сервера: {"status":"ok","data":["item1","item2"]}
3. Валидация и падения через middleware
Обёртка для проверки параметров перед вызовом обработчика.
function search_with_validation($keyword) {
if (empty($keyword) || strlen($keyword) < 2) {
http_response_code(400);
echo json_encode(['error' => 'Ключевое слово должно быть длиннее 2 символов']);
return;
}
// безопасный поиск
echo "Ищем: " . htmlspecialchars($keyword);
}
При запросе /search/a → 400 Bad Request и JSON с ошибкой.
4. Кеширование списка маршрутов (FastRoute)
Для повышения производительности в production.
$cacheFile = __DIR__ . '/route_cache.php';
if (file_exists($cacheFile)) {
$dispatcher = require $cacheFile;
} else {
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/search/{keyword}', 'search');
}, [
'cacheFile' => $cacheFile,
'cacheDisabled' => false,
]);
}
Кеш создаётся один раз, не требует перезагрузки при неизменных маршрутах.
5. Обработка UTF-8 и декодирование URL
$uri = urldecode($_SERVER['REQUEST_URI']);
// далее передаётся в маршрутизатор
$routeInfo = $dispatcher->dispatch($httpMethod, parse_url($uri, PHP_URL_PATH));
Это предотвращает проблемы с русскими буквами в адресе.
6. Резервирование fallback-маршрута для поиска
$r->addRoute('GET', '/product-search', 'redirect_to_search');
function redirect_to_search() {
header('Location: /search?q=' . urlencode($_GET['q'] ?? ''));
exit;
}
Удобно для старых ссылок.