Контроллеры и действия в Yii2: разбор на живом примере
Как в Yii2 устроен разбор маршрута до вызова действия, чем отличаются inline- и standalone-действия и где новички теряют данные между actionBefore и actionAfter.

Запрос /site/view?id=42 в приложении на Yii2 проходит длинный путь, прежде чем сработает метод actionView. По дороге фреймворк парсит маршрут, создаёт объект контроллера, ищет действие по имени, прогоняет его через фильтры и подставляет параметры из запроса в аргументы метода. Разберём этот путь на рабочем контроллере и покажем, где новички теряют данные и получают 404 там, где ждали работающую страницу. Материал написан для Yii2 (актуальная ветка 2.0.x); в Yii3 маршрутизация устроена иначе, здесь её не касаемся.
Что происходит между запросом и действием
Когда приходит HTTP-запрос, объект yii\web\Application вызывает runAction() с маршрутом вида site/view. Маршрут делится на две части: идентификатор контроллера (site) и идентификатор действия (view). Дальше работает UrlManager — он либо разбирает красивый URL по правилам, либо, при выключенном enablePrettyUrl, читает параметр r из строки запроса.
По идентификатору контроллера Yii ищет класс. Для site в стандартном приложении это app\controllers\SiteController. Правило именования жёсткое: имя класса — CamelCase от идентификатора плюс суффикс Controller. Файл SiteController.php лежит в директории, привязанной к пространству имён приложения.
После создания контроллера вызывается его метод runAction(), который ищет действие. Здесь начинается то, ради чего стоит понимать механизм: действий два вида, и путь поиска для них разный.
Два вида действий
Inline-действие — это метод контроллера с префиксом action. Идентификатор view превращается в метод actionView. Идентификатор с дефисами (create-post) — в actionCreatePost: дефисы срезаются, следующая буква поднимается в верхний регистр.
Standalone-действие — это отдельный класс, наследник yii\base\Action, зарегистрированный в методе actions(). Такое действие переиспользуется между контроллерами: капча, ошибка, загрузка файла. Классический пример из шаблона приложения:
<?php
// app/controllers/SiteController.php, Yii2 2.0.x
namespace app\controllers;
use Yii;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use app\models\Post;
class SiteController extends Controller
{
// Регистрация standalone-действий
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
// Inline-действие: /site/view?id=42
public function actionView($id)
{
$post = Post::findOne($id);
if ($post === null) {
throw new NotFoundHttpException('Запись не найдена');
}
return $this->render('view', ['post' => $post]);
}
}
При запросе /site/error сработает не метод, а класс ErrorAction. При /site/view — метод actionView. Если существуют и то и другое под одним идентификатором, приоритет у standalone-действия из actions() — inline-метод молча не вызовется. Это первая ловушка: объявили действие в actions(), забыли, потом дописали actionSomething и не понимаете, почему он не работает.
Как параметры попадают в аргументы
Строка public function actionView($id) работает не магией. Yii читает имена аргументов метода через рефлексию и подставляет одноимённые значения из $_GET (для веб-приложения — из параметров запроса). Аргумент $id получит значение из ?id=42.
Правила, которые важно держать в голове:
- Имя аргумента должно совпадать с именем параметра запроса.
$postIdне заполнится из?id=. - Если у аргумента нет значения по умолчанию и параметр не пришёл — Yii бросит
BadRequestHttpExceptionс текстом о недостающем параметре. Не 404, а именно 400. - Аргумент со значением по умолчанию (
$page = 1) необязателен. - Тип аргумента с подсказкой (
int $id) в PHP 7+ приведёт значение; несовместимый тип даст ошибку валидации параметра.
Самый частый вопрос новичка «почему $id всегда null» решается в одну секунду: имя аргумента метода и имя GET-параметра должны совпадать буква в букву. Yii связывает их по имени, а не по позиции.
Фильтры: что выполняется до и после действия
Между runAction и телом метода стоят фильтры (behaviors). Они объявляются в behaviors() и оборачивают действие: часть логики выполняется до, часть — после. Типичные — AccessControl (проверка прав) и VerbFilter (ограничение HTTP-методов).
<?php
// Фрагмент SiteController, Yii2 2.0.x
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'only' => ['create', 'delete'],
'rules' => [
[
'actions' => ['create', 'delete'],
'allow' => true,
'roles' => ['@'], // только авторизованные
],
],
],
'verbs' => [
'class' => VerbFilter::class,
'actions' => [
'delete' => ['POST'],
],
],
];
}
Порядок важен: фильтр доступа может прервать выполнение до того, как действие вообще стартует, и вернуть 403. Если действие не попадает в список only, фильтр его пропускает без проверки — легко открыть доступ туда, куда не собирался.
Ещё один слой — методы beforeAction() и afterAction() самого контроллера. beforeAction() должен вернуть true, иначе действие не выполнится. Классическая ошибка — переопределить его и забыть вызвать parent::beforeAction($action): сломаются встроенные проверки, например CSRF.
Полный путь запроса в одной таблице
| Этап | Кто отвечает | Что может пойти не так |
|---|---|---|
| Разбор URL | UrlManager | Нет правила — 404 ещё до контроллера |
| Поиск контроллера | Application | Неверное имя класса/файла — 404 |
| Поиск действия | Controller::runAction | Standalone перекрывает inline |
| Фильтры behaviors | ActionFilter | 403 от доступа, 405 от VerbFilter |
| beforeAction | Controller | Забыт parent:: — отвал CSRF |
| Привязка параметров | Controller | 400 при недостающем аргументе |
| Тело действия | Ваш метод | Логика приложения |
Когда выносить действие в отдельный класс
Inline-метод — по умолчанию. Он проще, весь код контроллера в одном файле, легко читать. Выносите в standalone, когда:
- одно и то же действие нужно в нескольких контроллерах (капча, ошибка, стандартная выгрузка);
- действие обросло зависимостями и настройками, которые логично задавать через свойства класса;
- его хочется покрыть отдельными тестами в изоляции от контроллера.
Для одноразовой страницы standalone-класс — оверинжиниринг: вы разносите логику по файлам без выигрыша.
Что настроить дальше
Разобравшись с маршрутом до действия, включите enablePrettyUrl и enableStrictParsing в UrlManager — со строгим разбором любой незарегистрированный маршрут даст честный 404, а не попытку угадать контроллер. Следующий логичный шаг — посмотреть, как действия отдают ответ: $this->render() против asJson() для API и настройка ContentNegotiator. Инвалидация фрагментного кеша во вьюхах — отдельная тема, здесь мы её не трогаем.
Разбор научной статьи: Сложный текст — понятным языком, с терминами
Спросить за 12 ₽Источники: Yii2 Guide — Controllers, Yii2 Guide — Filters
Частые вопросы
Почему аргумент $id в actionView всегда пустой?
Чем отличается 404 от 400 при обращении к действию?
У меня другая версия — Yii 1.1 или Yii3, всё так же?
Почему мой actionUpload не вызывается, хотя метод есть?
Я переопределил beforeAction и перестали проходить POST-формы. В чём дело?
Как ограничить действие только POST-запросами?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.