Маршрутизация и красивые URL в Yii: разбор на живом примере
Как работает UrlManager в Yii 2, чем отличается PATH от GET-параметров и почему один неудачно расположенный роут ломает все остальные. Разбираем на примере блога с категориями.

Магазин на Yii 2 отдавал товары по адресам вида index.php?r=product/view&id=42, а маркетолог просил /catalog/nabor-otvertok. После включения ЧПУ половина ссылок заработала, а /catalog/all вдруг стала открывать карточку товара с несуществующим id. Причина — в порядке правил UrlManager и в том, как Yii разбирает входящий путь. Разберём механизм на живом примере: блог с категориями, постами и пагинацией.
Всё, что ниже, проверено на Yii 2.0.49. В Yii 3 маршрутизация устроена иначе (отдельный пакет yiisoft/router поверх PSR-7), и правила из этой статьи туда не переносятся напрямую.
Как Yii превращает URL в вызов контроллера
За разбор входящего адреса и генерацию ссылок отвечает компонент urlManager. Он работает в двух направлениях:
- Парсинг — из строки запроса получить пару «маршрут + параметры». Вызывается один раз на каждый HTTP-запрос, до того как приложение решит, какой контроллер запускать.
- Создание — из маршрута и параметров собрать строку URL. Вызывается каждый раз, когда вы пишете
Url::to(['post/view', 'id' => 42])илиHtml::a().
Ключевой момент: одни и те же правила работают в обе стороны. Если правило умеет разобрать /blog/42, оно же соберёт эту ссылку обратно. Поэтому нельзя тестировать только открытие страниц — проверяйте и генерацию ссылок в шаблонах.
Базовая настройка
Без ЧПУ Yii отдаёт адреса вида index.php?r=post/view&id=1. Красивые URL включаются в конфигурации приложения, в секции components:
// config/web.php (Yii 2.0.49)
'urlManager' => [
// включает режим PATH вместо ?r=...
'enablePrettyUrl' => true,
// убирает index.php из адреса; требует правил в веб-сервере
'showScriptName' => false,
// строгий разбор: несуществующий путь даёт 404, а не молчаливый роут
'enableStrictParsing' => false,
'rules' => [
// правила добавим ниже
],
],
Чтобы showScriptName => false сработал, веб-сервер должен направлять все несуществующие пути на index.php. Для Nginx это try_files $uri $uri/ /index.php?$args;, для Apache — .htaccess с RewriteRule. Без этого вы получите 404 от сервера, ещё не дойдя до Yii.
Живой пример: блог с категориями
Задача — три типа адресов:
/blog— список всех постов,post/index;/blog/category/tech— посты категории по слагу,post/category;/blog/hello-world— конкретный пост по слагу,post/view.
Первая, наивная попытка описать это выглядит так:
'rules' => [
'blog' => 'post/index',
'blog/<slug>' => 'post/view',
'blog/category/<slug>' => 'post/category',
],
И она сломана. Запрос /blog/category/tech Yii проверяет правила сверху вниз и останавливается на первом совпавшем. Правило blog/<slug> совпадёт с blog/category ещё до того, как очередь дойдёт до третьей строки: <slug> по умолчанию матчит любой сегмент, включая слово category. В итоге контроллер получит пост со слагом category, а tech потеряется.
Правила UrlManager — это не набор, а очередь. Первое совпавшее правило выигрывает и забирает запрос себе. Специфичное всегда ставится выше общего.
Порядок и ограничения на параметры
Чинится двумя приёмами: правильным порядком и ограничением того, что попадает в <slug>. Более специфичное правило (blog/category/...) поднимаем выше, а на слаг поста вешаем регулярное выражение через синтаксис <имя:шаблон>:
'rules' => [
'blog' => 'post/index',
// категория идёт раньше поста и имеет фиксированный префикс
'blog/category/<slug:[\w-]+>' => 'post/category',
// слаг поста — только буквы, цифры и дефис
'blog/<slug:[\w-]+>' => 'post/view',
],
Теперь /blog/category/tech совпадёт с вторым правилом до того, как дойдёт до третьего. А если кто-то запросит /blog/category без слага — ни второе, ни третье правило не подойдут (второму нужен сегмент после category, третьему [\w-]+ не даст совпасть на строке с лишним сегментом), и Yii отдаст 404. Это правильное поведение.
Проверка, что получилось: откройте /blog/category/tech — должен запуститься PostController::actionCategory() с $slug === 'tech'. Откройте /blog/hello-world — actionView() с $slug === 'hello-world'.
Действие в контроллере
Параметр из правила приходит в метод действия как обычный аргумент — по имени:
// PostController.php
public function actionView($slug)
{
$post = Post::findOne(['slug' => $slug]);
if ($post === null) {
// корректный 404 вместо пустой страницы
throw new \yii\web\NotFoundHttpException('Пост не найден');
}
return $this->render('view', ['post' => $post]);
}
Имя параметра в правиле (<slug>) должно совпадать с именем аргумента метода. Если назвать в правиле <name>, а в методе оставить $slug, Yii передаст null и запрос молча уйдёт в ошибку «пост не найден».
Генерация ссылок в обратную сторону
Правила работают и на создание URL. В шаблоне вы пишете маршрут и параметры, а urlManager подбирает подходящее правило:
use yii\helpers\Url;
use yii\helpers\Html;
// вернёт /blog/hello-world
echo Url::to(['post/view', 'slug' => 'hello-world']);
// ссылка на категорию
echo Html::a('Технологии', ['post/category', 'slug' => 'tech']);
Yii ищет первое правило, которое умеет собрать маршрут post/view с параметром slug. Если такого правила нет — вернётся адрес в старом формате ?r=post/view&slug=.... Это частая причина «почему у меня половина ссылок красивые, а половина нет»: для этих маршрутов правило просто не описано.
Пагинация и лишние параметры
Если параметр не назван в правиле, Yii дописывает его в query-строку. Так удобно ловить пагинацию:
// /blog?page=2 — page уходит в GET, правило blog его не съедает
echo Url::to(['post/index', 'page' => 2]);
Отдельного правила для page не нужно — yii\data\Pagination сам добавит и прочитает этот параметр из строки запроса.
Три способа задать правило и когда какой брать
Строка 'шаблон' => 'маршрут' — не единственный формат. Для нетривиальных случаев есть развёрнутый массив и отдельный класс правила.
| Способ | Когда брать | Цена |
|---|---|---|
Строковое правило 'blog/<slug>' => 'post/view' | Один маршрут, фиксированный шаблон | Ничего не настраивается сверх шаблона |
Массив с pattern, route, defaults, verb | Нужно ограничить HTTP-метод, задать значения по умолчанию, режим только-парсинг | Многословнее, но всё в конфиге |
Свой класс, реализующий UrlRuleInterface | Слаг зависит от данных в БД, нужна подмена по справочнику, сложная логика | Отдельный класс, свой кеш, риск лишних запросов к БД на каждый парсинг |
Массивный формат для правила, которое отвечает только на POST:
[
'pattern' => 'blog/<slug:[\w-]+>/comment',
'route' => 'post/comment',
'verb' => 'POST',
],
Свой класс правила берите только когда слаг нельзя описать регуляркой — например, категория и пост живут в одном пространстве адресов и различить их можно лишь запросом в базу. Помните про цену: метод parseRequest() вызывается на каждом запросе, и запрос к БД внутри него бьёт по времени ответа всего приложения.
Что ломается чаще всего
- Правило-ловушка сверху. Общее
<controller>/<action>илиblog/<slug>выше специфичных перехватывает всё. Специфичное — всегда выше. - Слаг без ограничения.
<slug>без:[\w-]+матчит любой один сегмент, включая служебные слова. - Кеш конфигурации. После правки правил в проде с включённым
OPcacheили кешированием конфигурации изменения не подхватываются, пока не сбросите кеш. - Несовпадение имён. Имя в
<...>обязано совпасть с именем аргумента действия, иначе параметр приходит пустым. - Забытый веб-сервер.
showScriptName => falseбез правил переадресации в Nginx/Apache даёт 404 на любом красивом адресе.
Что делать дальше
Когда базовые правила заработали, включите enableStrictParsing => true: тогда всё, что не описано явным правилом, отдаёт 404, а не проваливается в маршрут по умолчанию. Это защищает от случайно доступных действий. Отдельная большая тема — версионирование и слаги для REST через yii\rest\UrlRule; здесь мы её не трогали. В проде смотрите на долю запросов, уходящих в ?r=...: если она не ноль при enablePrettyUrl, значит для каких-то маршрутов не описаны правила и ссылки собираются в старом формате.
Тест и проверочная: Быстрые ответы с объяснением, почему остальные варианты неверны
Спросить за 10 ₽Источники: Yii 2 Guide — Routing and URL Creation, yii\web\UrlManager — Class Reference
Частые вопросы
У меня Yii 3, эти правила подойдут?
Включил enablePrettyUrl, но все адреса дают 404. Что не так?
Правило не срабатывает, хотя выглядит правильным. Куда смотреть?
Как ограничить, какие символы попадают в слаг?
Часть ссылок красивые, часть в формате ?r=... — почему?
Опасно ли делать запрос к БД внутри своего класса правила?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.