Создание маршрута заказа аккаунта с поддержкой мультиязычности

Раздел: Веб-разработка на PHP -> Роутинг в PHP

Реализация маршрута заказа аккаунта с языковым префиксом

Рассмотрим задачу: на сайте с поддержкой русского языка необходимо обрабатывать URL вида /ru/index.php?route=account/order или ЧПУ вида /ru/account/order. Требуется написать систему роутинга, которая будет направлять запрос к соответствующему контроллеру.

Как создать собственный роутер с поддержкой языкового префикса и параметров?

Наиболее гибкое и контролируемое решение – написать класс Router вручную. Это позволяет точно настроить обработку URL и не зависеть от внешних библиотек.

// Класс Router
class Router {
    private array $routes = [];
    
    public function add(string $pattern, callable $handler): void {
        // Преобразуем шаблон в регулярное выражение
        $pattern = preg_replace('/\{([a-z]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $this->routes[] = [
            'pattern' => '/^' . str_replace('/', '\/', $pattern) . '$/',
            'handler' => $handler
        ];
    }
    
    public function dispatch(string $uri): void {
        $uri = parse_url($uri, PHP_URL_PATH);
        foreach ($this->routes as $route) {
            if (preg_match($route['pattern'], $uri, $matches)) {
                // Извлекаем только именованные параметры
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                call_user_func($route['handler'], $params);
                return;
            }
        }
        // 404
        http_response_code(404);
        echo 'Страница не найдена';
    }
}

// Использование
$router = new Router();
$router->add('/{lang}/account/order', function($params) {
    echo 'Язык: ' . $params['lang'] . ' – Страница заказа аккаунта';
});
$router->add('/{lang}/account/login', function($params) {
    echo 'Язык: ' . $params['lang'] . ' – Вход в аккаунт';
});
$router->dispatch($_SERVER['REQUEST_URI']);

Ru index php route account order (маршрут заказа аккаунта (ru))

Пояснение шагов:

  1. Метод add принимает шаблон с плейсхолдерами в фигурных скобках и заменяет их на именованные группы в регулярном выражении.
  2. Метод dispatch получает URI, отбрасывает параметры запроса и последовательно проверяет маршруты.
  3. При совпадении вызывается обработчик с массивом параметров.
  4. Если маршрут не найден – возвращается ошибка 404.

Типичные проблемы и их решение:

  • Порядок маршрутов: Если один шаблон является подстрокой другого, менее специфичный маршрут может перехватить запрос. Решение – размещать более конкретные маршруты выше.
  • Символы в URI: Регулярное выражение может не учитывать слеши или специальные символы. Необходимо экранировать слеши и использовать правильные классы символов.
  • Производительность: При большом количестве маршрутов последовательный поиск может быть медленным. Решение – использовать статический анализ или кэшировать скомпилированные маршруты.

Как реализовать роутинг через обычный switch/case для фиксированных маршрутов?

Для простых сайтов с небольшим числом статических маршрутов можно обойтись конструкцией switch. Это самый прямолинейный способ.

$uri = $_SERVER['REQUEST_URI'];
$path = parse_url($uri, PHP_URL_PATH);

switch ($path) {
    case '/ru/account/order':
        echo 'Заказ аккаунта (рус)';
        break;
    case '/en/account/order':
        echo 'Order account (en)';
        break;
    default:
        http_response_code(404);
        echo 'Страница не найдена';
        break;
}

Php route product manufacturer (маршрут php для продукта производителя)

Цель – быстрое развертывание без дополнительных классов. Случаи использования – прототипы, тестовые проекты.

Проблемы:

  • Отсутствие динамических параметров – каждый маршрут жестко задан.
  • При добавлении новых языков или страниц код разрастается.
  • Невозможность обработки ЧПУ с переменными частями.

Как применить регулярные выражения напрямую без класса?

Можно использовать ассоциативный массив шаблонов и обработчиков, перебирая его.

$routes = [
    '#^/([a-z]{2})/account/order$#' => function($m) { echo "Язык: $m[1] – заказ"; },
    '#^/([a-z]{2})/account/login$#' => function($m) { echo "Язык: $m[1] – вход"; },
];

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$found = false;
foreach ($routes as $pattern => $handler) {
    if (preg_match($pattern, $uri, $matches)) {
        $handler($matches);
        $found = true;
        break;
    }
}
if (!$found) {
    http_response_code(404);
    echo 'Не найдено';
}

Class route php (класс маршрутизации в php)

Этот подход компактнее switch, но всё ещё не структурирован. Подходит для небольших проектов с известными шаблонами.

Проблемы:

  • Трудно поддерживать сложные маршруты с несколькими параметрами.
  • Отсутствует проверка HTTP методов (GET, POST).
  • Код повторяется для разных маршрутов.

Как использовать библиотеку AltoRouter для организации маршрута account/order?

AltoRouter – популярная библиотека с простым API. Позволяет задавать именованные маршруты, поддерживает фильтры.

// Установка: composer require altorouter/altorouter
require 'vendor/autoload.php';
$router = new AltoRouter();

// Добавляем маршруты
$router->map('GET', '/[a:lang]/account/order', function($lang) {
    echo "Язык: $lang – заказ";
});

$router->map('GET', '/[a:lang]/account/order/[i:id]', function($lang, $id) {
    echo "Язык: $lang, ID: $id";
});

// Обработка
$match = $router->match();
if ($match && is_callable($match['target'])) {
    call_user_func_array($match['target'], $match['params']);
} else {
    header('HTTP/1.0 404 Not Found');
    echo '404';
}

Php route login (маршрут входа в php)

Цель – использование проверенного решения с минимальным кодом. Подходит для проектов средней сложности.

Проблемы:

  • Необходимость установки через Composer.
  • Ограниченная гибкость в определении сложных шаблонов.
  • При большом количестве маршрутов может быть менее производительным, чем FastRoute.

Как реализовать роутинг с помощью FastRoute для высокой производительности?

FastRoute – одна из самых быстрых библиотек, использует скомпилированные регулярные выражения. Рекомендуется для высоконагруженных проектов.

// Установка: composer require nikic/fast-route
require 'vendor/autoload.php';

$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
    $r->addRoute('GET', '/{lang:[a-z]{2}}/account/order', function($lang) {
        echo "Язык: $lang – заказ";
    });
    $r->addRoute('GET', '/{lang}/account/order/{id:\d+}', function($lang, $id) {
        echo "Язык: $lang, ID: $id";
    });
});

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routeInfo = $dispatcher->dispatch($_SERVER['REQUEST_METHOD'], $uri);

switch ($routeInfo[0]) {
    case FastRoute\Dispatcher::FOUND:
        $handler = $routeInfo[1];
        $vars = $routeInfo[2];
        $handler(...$vars);
        break;
    case FastRoute\Dispatcher::NOT_FOUND:
        http_response_code(404);
        echo '404 Not Found';
        break;
    case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
        http_response_code(405);
        echo '405 Method Not Allowed';
        break;
}

Routing api php (маршрутизация api в php)

Цель – максимальная скорость и поддержка сложных маршрутов. Используется в крупных проектах, фреймворках.

Проблемы:

  • Требуется установка через Composer.
  • Меньше встроенных функций по сравнению с AltoRouter (например, нет именованных маршрутов).
  • Необходимость самостоятельно формировать ответы.

Как организовать роутинг в стиле MVC с использованием Front Controller?

В этом подходе все запросы направляются в единую точку входа (index.php), где роутер разбирает URL и вызывает соответствующий контроллер. Подходит для фреймворков.

// index.php
require 'Router.php';
$router = new Router();
$router->add('GET', '/{lang}/account/order', 'AccountController@order');
$router->add('GET', '/{lang}/account/login', 'AccountController@login');

$result = $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
// Внутри Router создается экземпляр AccountController и вызывается метод order($lang)

Подробнее: Router парсит URL, определяет контроллер и метод, создает объект и вызывает метод с параметрами. Это основа многих фреймворков (Laravel, Symfony).

Проблемы:

  • Необходимо реализовать автозагрузку классов.
  • Требуется продумать соглашения об именовании контроллеров.
  • Сложность с внедрением зависимостей.

Расширенные примеры реализации роутинга для маршрута account/order

Пример собственного роутера с поддержкой HTTP методов и middleware

Реализуем роутер, который учитывает метод запроса и позволяет добавить промежуточные функции (middleware) для проверки языкового префикса.

Пример
class Router {
    private array $routes = [];
    
    public function add(string $method, string $pattern, callable $handler, array $middleware = []): void {
        $pattern = preg_replace('/\{([a-z]+)\}/', '(?P<$1>[^/]+)', $pattern);
        $this->routes[] = [
            'method' => strtoupper($method),
            'pattern' => '/^' . str_replace('/', '\/', $pattern) . '$/',
            'handler' => $handler,
            'middleware' => $middleware
        ];
    }
    
    public function dispatch(string $method, string $uri): void {
        $uri = parse_url($uri, PHP_URL_PATH);
        $method = strtoupper($method);
        foreach ($this->routes as $route) {
            if ($route['method'] === $method && preg_match($route['pattern'], $uri, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                // Выполняем middleware
                foreach ($route['middleware'] as $mw) {
                    if (call_user_func($mw, $params) === false) {
                        return; // middleware прервало выполнение
                    }
                }
                call_user_func($route['handler'], $params);
                return;
            }
        }
        http_response_code(404);
        echo 'Не найдено';
    }
}

// Middleware для проверки языка
$langMiddleware = function($params) {
    $allowed = ['ru', 'en'];
    if (!isset($params['lang']) || !in_array($params['lang'], $allowed)) {
        http_response_code(400);
        echo 'Недопустимый язык';
        return false;
    }
    return true;
};

$router = new Router();
$router->add('GET', '/{lang}/account/order', function($p) {
    echo "Заказ для языка: {$p['lang']}";
}, [$langMiddleware]);

$router->dispatch('GET', '/ru/account/order');

Результат выполнения:

Заказ для языка: ru

Если указать неверный язык, например /fr/account/order, middleware вернет false и будет выведено сообщение об ошибке 400.

Пример с FastRoute и автоматической генерацией URL

FastRoute не предоставляет генерацию URL, но можно создать вспомогательную функцию, используя обратное преобразование шаблона. Реализуем простой генератор на основе имени маршрута.

Пример
// Определим маршруты с именами
$routes = [
    'order' => ['method' => 'GET', 'pattern' => '/{lang}/account/order', 'handler' => function($lang) { echo "Order $lang"; }],
    'order_with_id' => ['method' => 'GET', 'pattern' => '/{lang}/account/order/{id:\d+}', 'handler' => function($lang, $id) { echo "Order $lang $id"; }],
];

// Функция генерации URL
function generateUrl(string $name, array $params = []): string {
    global $routes;
    if (!isset($routes[$name])) {
        throw new Exception('Route not found');
    }
    $url = $routes[$name]['pattern'];
    foreach ($params as $key => $value) {
        $url = str_replace('{' . $key . ':?[^/]+?}', $value, $url);
        $url = str_replace('{' . $key . '}', $value, $url);
    }
    return $url;
}

// Использование FastRoute
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) use ($routes) {
    foreach ($routes as $name => $route) {
        $r->addRoute($route['method'], $route['pattern'], $route['handler']);
    }
});

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routeInfo = $dispatcher->dispatch('GET', $uri);

if ($routeInfo[0] === FastRoute\Dispatcher::FOUND) {
    $handler = $routeInfo[1];
    $vars = $routeInfo[2];
    $handler(...$vars);
}

// Пример генерации ссылки
echo 'Ссылка на заказ: ' . generateUrl('order', ['lang' => 'ru']);

Результат:

Ссылка на заказ: /ru/account/order

Такой подход позволяет централизованно управлять URL и избегать жесткого прописывания путей в шаблонах.

Пример с AltoRouter и именованными маршрутами для мультиязычного сайта

AltoRouter позволяет задать имя маршруту и затем генерировать URL по имени. Это удобно для создания ссылок на странице.

Пример
require 'vendor/autoload.php';
$router = new AltoRouter();

// Добавляем маршрут с именем 'account_order'
$router->map('GET', '/[a:lang]/account/order', function($lang) {
    echo "Order page for $lang";
}, 'account_order');

// Генерация URL
$urlRu = $router->generate('account_order', ['lang' => 'ru']);
$urlEn = $router->generate('account_order', ['lang' => 'en']);

echo 'Ссылка на русском: ' . $urlRu . '
'; echo 'Ссылка на английском: ' . $urlEn . '
'; // Выполнение роутинга $match = $router->match(); if ($match && is_callable($match['target'])) { call_user_func_array($match['target'], $match['params']); }

Результат:

Ссылка на русском: /ru/account/order
Ссылка на английском: /en/account/order

При запуске скрипта по адресу /ru/account/order будет выведено: "Order page for ru".

Пример обработки опциональных параметров (например, id) через регулярные выражения

Иногда параметр может отсутствовать. Создадим роутер, который поддерживает необязательные сегменты.

Пример
$routes = [
    '#^/([a-z]{2})/account/order(?:/(\d+))?$#' => function($m) {
        $lang = $m[1];
        $id = isset($m[2]) ? $m[2] : null;
        echo "Язык: $lang. " . ($id ? "ID заказа: $id" : "Все заказы");
    }
];

$uri = '/ru/account/order/42';
preg_match(key($routes), $uri, $matches);
$routes[key($routes)]($matches); // Вывод: Язык: ru. ID заказа: 42

$uri2 = '/ru/account/order';
preg_match(key($routes), $uri2, $matches);
$routes[key($routes)]($matches); // Вывод: Язык: ru. Все заказы

Результат:

Для URI /ru/account/order/42: Язык: ru. ID заказа: 42
Для URI /ru/account/order: Язык: ru. Все заказы

Использование нежадных квантификаторов и групп без захвата позволяет гибко обрабатывать опциональные части URL.

Маршрут заказа аккаунта (ru) - comments

En
Ru index php route account order (php)