Контроллеры являются частью [MVC](https://ru.wikipedia.org/wiki/Model-View-Controller) архитектуры. Это объекты классов, унаследованных
от [[yii\base\Controller]] и отвечающие за обработку запроса и генерирование ответа. В сущности, после обработки запроса [приложениями](structure-applications.md),
контроллеры проанализируют входные данные, передадут их в [модели](structure-models.md), вставят результаты модели в [представления](structure-views.md),
и в конечном итоге сгенерируют исходящие ответы.
## Действия <a name="actions"></a>
Контроллеры состоят из *действий*, которые являются основными блоками, к которым может обращаться конечный пользователь и запрашивать исполнение того или иного
функционала. В контроллере может быть одно или несколько действий.
Следующий пример показывает `post` контроллер с двумя действиями: `view` и `create`:
Вы можете сконфигурировать [[yii\base\Application::controllerMap|карту контроллеров]] для того, чтобы преодолеть
описанные выше ограничения именования ID контроллеров и названий классов. В основном это очень удобно, когда вы используете
сторонние контроллеры, именование которых вы не можете контроллировать.
Вы можете сконфигурировать [[yii\base\Application::controllerMap|карту контроллеров]] в [настройках приложения](structure-applications.md#application-configurations)
следующим образом:
```php
[
'controllerMap'=>[
[
// объявляет "account" контроллер, используя название класса
'account'=>'app\controllers\UserController',
// объявляет "article" контроллер, используя массив конфигурации
Каждое приложение имеет контроллер по-умолчанию, указанный через свойство [[yii\base\Application::defaultRoute]].
Когда в запросе не указан [маршрут](#ids-routes), тогда будет использован маршрут указанный в данном свойстве.
Для [[yii\web\Application|Веб приложений]], это значение `'site'`, в то время как для [[yii\console\Application|консольных приложений]],
это `'help'`. Таким образом, если задан URL `http://hostname/index.php`, это означает, что контроллер `site` выполнит обработку запроса.
Вы можете изменить контроллер по-умолчанию следующим образом в [настройках приложения](structure-applications.md#application-configurations):
```php
[
'defaultRoute'=>'main',
]
```
## Создание действий <a name="creating-actions"></a>
Создание действий не представляет сложностей также как и объявление так называемых *методов действий* в классе контроллера. Метод действия
это *public* метод, имя которого начинается со слова `action`. Возвращаемое значение метода действия представляет собой ответные данные,
которые будут высланы конечному пользователю. Приведенный ниже код определяет два действия `index` и `hello-world`:
```php
namespaceapp\controllers;
useyii\web\Controller;
classSiteControllerextendsController
{
publicfunctionactionIndex()
{
return$this->render('index');
}
publicfunctionactionHelloWorld()
{
return'Hello World';
}
}
```
### ID действий <a name="action-ids"></a>
В основном действие разрабатывается для какой-либо конкретной обработки ресурса. По этой причине, ID действий в основном
являются глаголами, такими как `view`, `update`, и т. д.
По-умолчанию, ID действия должен содержать только следующие символы: Английские буквы в нижнем регистре, цифры,
подчеркивания и дефисы. Дефисы в ID действий используются для разделения слов. Например, `view`, `update2`, `comment-post` являются
допустимыми ID действий, в то время как `view?`, `Update` не являются таковыми.
Вы можете создавать действия двумя способами: встроенные действия и отдельные действия. Встроенное действие является методом, определенным
в классе контроллера, в то время как отдельное действие является экземпляром класса, унаследованного от [[yii\base\Action]] или его потомков.
Встроенные действия требуют меньше усилий для создания и в основном используются если у вас нет надобности в повторном использовании действий.
Отдельные действия, с другой стороны, в основном создаются для использования в различных контроллерах или при использовании в [расширениях](structure-extensions.md).
### Встроенные действия <a name="inline-actions"></a>
Встроенные действия это те действия, которые определены в рамках методов контроллера, как мы это уже обсудили.
Названия методов действий могут быть получены из ID действий следующим образом:
* Привести первый символ каждого слова в ID действия в верхний регистр;
* Убрать дефисы;
* Добавить префикс `action`.
Например, `index` соответствует `actionIndex`, а `hello-world` соответствует `actionHelloWorld`.
> Примечание: Названия имен действий являются *регистрозависимыми*. Если у вас есть метод `ActionIndex`, он не будет
учтен как метод действия, таким образом, запрос к действию `index` приведет к выбросу исключению. Также следует учесть, что методы действий
должны иметь область видимости public. Методы имеющие область видимости private или protected НЕ определяют методы встроенных действий.
Встроенные действия в основном используются, потому что для их создания не нужного много усилий. Тем не менее, если вы планируете повторно
использовать некоторые действия в различных местах, или если вы хотите перераспределить действия, вы должны определить его как *отдельной действие*.
### Отдельные действия <a name="standalone-actions"></a>
Отдельные действия определяются в качестве классов, унаследованных от [[yii\base\Action]] иди его потомков.
Например, в Yii релизах, присутствуют [[yii\web\ViewAction]] и [[yii\web\ErrorAction]], оба из которых являются
отдельными действиями.
Для использования отдельного действия, вы должны указать его в *карте действий*, с помощью переопределения метода
[[yii\base\Controller::actions()]] в вашем классе контроллера, следующим образом:
```php
publicfunctionactions()
{
return[
// объявляет "error" действие с помощью названия класса
'error'=>'yii\web\ErrorAction',
// объявляет "view" действие с помощью конфигурационного массива
'view'=>[
'class'=>'yii\web\ViewAction',
'viewPrefix'=>'',
],
];
}
```
Как вы можете видеть, метод `actions()` должен вернуть массив, ключами которого являются ID действий, а значениями - соответствующие
названия класса действия или [конфигурация](concept-configurations.md). В отличие от встроенных действий, ID отдельных действий могут
содержать произвольные символы, до тех пор пока они определены в методе `actions()`.
Для создания отдельного действия, вы должны унаследоваться от класса [[yii\base\Action]] или его потомков, и реализовать
метод `run()` с областью видимости public. Роль метода `run()` аналогична другим методам действий. Например,
```php
<?php
namespaceapp\components;
useyii\base\Action;
classHelloWorldActionextendsAction
{
publicfunctionrun()
{
return"Hello World";
}
}
```
### Результаты действий <a name="action-results"></a>
Возвращаемое значение методов действий или метода `run()` отдельного действия очень важно. Оно является результатом
выполнения соответствующего действия.
Возвращаемое значение может быть объектом [response](runtime-responses.md), который будет отослан конечному пользователю
в качестве ответа.
* Для [[yii\web\Application|Веб приложений]], возвращаемое значение также может быть произвольными данными, которые будут
присвоены [[yii\web\Response::data]], а затем сконвертированы в строку, представляющую тело ответа.
* Для [[yii\console\Application|Консольных приложений]], возвращаемое значение также может быть числом, представляющим
[[yii\console\Response::exitStatus|статус выхода]] исполнения команды.
В вышеприведенных примерах, все результаты действий являются строками, которые будут использованы в качестве тела ответа,
высланного пользователю. Следующий пример, показывает действие может перенаправить браузер пользователя на новый URL, с помощью
возврата response объекта (т. к. [[yii\web\Controller::redirect()|redirect()]] метод возвращает response объект):
```php
publicfunctionactionForward()
{
// перенаправляем браузер пользователя на http://example.com
return$this->redirect('http://example.com');
}
```
### Параметры действий <a name="action-parameters"></a>
Методы действий для встроенных действий и методы `run()` для отдельных действий могут принимать параметры,
называемые *параметры действий*. Их значения беруться из запросов. Для [[yii\web\Application|Веб приложений]],
значение каждого из параметров действия берется из `$_GET`, используя название параметра в качестве ключа;
для [[yii\console\Application|консольных приложений]], они соответствуют аргументам командной строки.
В приведенном ниже примере, действие `view` (встроенное действие) определяет два параметра: `$id` и `$version`.
```php
namespaceapp\controllers;
useyii\web\Controller;
classPostControllerextendsController
{
publicfunctionactionView($id,$version=null)
{
// ...
}
}
```
Для разных запросов параметры действий будут определены следующим образом:
*`http://hostname/index.php?r=post/view&id=123`: параметр `$id` будет присвоено значение `'123'`, в то время
как `$version` будет иметь значение null, т. к. строка запроса не содержит параметра `version`;
*`http://hostname/index.php?r=post/view&id=123&version=2`: параметрам `$id` и `$version` будут присвоены
значения `'123'` и `'2'` соответственно;
*`http://hostname/index.php?r=post/view`: будет брошено исключение [[yii\web\BadRequestHttpException]], т. к.
обязательный праметр `$id` не был указан в запросе;
*`http://hostname/index.php?r=post/view&id[]=123`: будет брошено исключение [[yii\web\BadRequestHttpException]], т. к.
параметр `$id` получил неверное значение `['123']`.
Если вы хотите, чтобы параметр действия принимал массив значений, вы должны использовать type-hint значение `array`, как показано ниже:
```php
publicfunctionactionView(array$id,$version=null)
{
// ...
}
```
Теперь, если запрос будет содержать URL `http://hostname/index.php?r=post/view&id[]=123`, то параметр `$id` примет значение
`['123']`. Если запрос будет содержать URL `http://hostname/index.php?r=post/view&id=123`, то параметр `$id` все равно будет
содержать массив, т. к. скалярное значение `'123'` будет автоматически сконвертировано в массив.
Вышеприведенные примеры в основном показывают как параметры действий работают для Веб приложений. Больше информации
о параметрах консольных приложений представлено в секции [Консольные команды](tutorial-console.md).
### Действие по-умолчанию <a name="default-action"></a>
Каждый контроллер имеет действие, указанное через свойство [[yii\base\Controller::defaultAction]].
Когда [маршрут](#ids-routes) содержит только ID контроллера, то подразумевается, что действие контроллера по-умолчанию
было запрошено.
По-умолчанию, это действие имеет значение `index`. Если вы хотите изменить это значение, просто переопределите данное
свойство в классе контроллера следующим образом:
```php
namespaceapp\controllers;
useyii\web\Controller;
classSiteControllerextendsController
{
public$defaultAction='home';
publicfunctionactionHome()
{
return$this->render('home');
}
}
```
## Жизненный цикл контроллера <a name="controller-lifecycle"></a>
При обработке запроса, [приложение](structure-applications.md) создаст контроллер, основываясь на
запрошенном [маршруте](#routes). Для выполнения запроса, контроллер пройдет через следующие этапы
жизненного цикла:
1. Метод [[yii\base\Controller::init()]] будет вызван после того как контроллер будет создан и сконфигурирован;
2. Контроллер создает объект действия, основываясь на запрошенном ID действия:
* Если ID действия не указан, то будет использовано [[yii\base\Controller::defaultAction|ID действия по-умолчанию]];
* Если ID действия найдено в [[yii\base\Controller::actions()|карте действий]], то отдельное действие будет создано;
* Если ID действия соответствует методу действия, то встроенное действие будет создано;
* В противном случае, будет выброшено исключение [[yii\base\InvalidRouteException]].
3. Контроллер последовательно вызывает метод `beforeAction()` приложения, модуля (если контроллер принадлежит модулю) и
самого контроллера.
* Если один из методов вернул `false`, то остальные, невызванные методы `beforeAction` будут пропущены, а выполнение
действия будет отменено;
* По-умолчанию, каждый вызов метода `beforeAction()` вызовет событие `beforeAction`, на которое вы можете назначить обработчики.
4. Контроллер запускает действие:
* Параметры действия будут проанализированы и заполенены из данных запроса.
5. Контроллер последовательно вызывает методы `afterAction` контроллера, модуля (если контроллер принадлежит модулю) и приложения.
* По-умолчанию, каждый вызов метода `afterAction()` вызовет событие `afterAction`, на которое вы можете назначить обработчики.
6. Приложение, получив результат выполнения действия, присвоит его объекту [response](runtime-responses.md).
## Лучшие практики <a name="best-practices"></a>
В хорошо-организованных приложения, контроллеры обычно очень тонкие, и содержат лишь несколько строк кода.
Если ваш контроллер слишком сложный, это обычно означает, что вам надо провести рефакториг его и перенести какой-либо код
в другие места.
В целом, контроллеры
* могут иметь доступ к данным [запроса]runtime-requests.md);
* могут вызывать методы [моделей](structure-models.md) и других компонентов системы с данными запроса;
* могут использовать [представления](structure-views.md) для формирования ответа;
* не должны заниматься обработкой данных, это должно происходить в [моделях](structure-models.md);
* должны избегать использования HTML или другой разметки, лучше это делать в [представлениях](structure-views.md).