Контроллер
==========
`Контроллер (controller)` — это экземпляр класса [CController] или унаследованного
от него класса. Он создается объектом приложения в случае, когда пользователь его
запрашивает. При запуске контроллер выполняет соответствующее действие, что обычно
подразумевает создание соответствующих моделей и отображение необходимых представлений.
В самом простом случае `действие` — это метод класса контроллера, название
которого начинается на `action`.

У контроллера есть действие по умолчанию, которое выполняется в случае, когда
пользователь не указывает действие при запросе. По умолчанию это действие
называется `index`. Изменить его можно путём установки значения [CController::defaultAction].

Следующий код определяет контроллер `site` с действиями `index` (действие по
умолчанию) и `contact`:

~~~
[php]
class SiteController extends CController
{
	public function actionIndex()
	{
		// ...
	}

	public function actionContact()
	{
		// ...
	}
}
~~~


Маршрут
-------
Контроллеры и действия опознаются по их идентификаторам.
Идентификатор контроллера — это запись формата `path/to/xyz`, соответствующая
файлу класса контроллера `protected/controllers/path/to/XyzController.php`, где `xyz`
следует заменить реальным названием класса (например, `post` соответствует
`protected/controllers/PostController.php`). Идентификатор действия — это название
метода без префикса `action`. Например, если класс контроллера содержит метод
`actionEdit`, то идентификатор соответствующего действия — `edit`.

Пользователь обращается к контроллеру и действию посредством маршрута (route).
Маршрут формируется путём объединения идентификаторов контроллера и действия,
отделенных косой чертой. Например, маршрут `post/edit` указывает на действие
`edit` контроллера `PostController`, и по умолчанию URL `http://hostname/index.php?r=post/edit`
приведёт к вызову именно этих контроллера и действия.

> Note|Примечание: По умолчанию маршруты чувствительны к регистру.
>Это возможно изменить путём установки свойства
>[CUrlManager::caseSensitive] равным false в конфигурации приложения.
>В режиме, не чувствительном к регистру, убедитесь, что названия директорий,
>содержащих файлы классов контроллеров, указаны в нижнем регистре, а также,
>что [controller map|CWebApplication::controllerMap] и [action map|CController::actions]
>используют ключи в нижнем регистре.

Приложение может содержать [модули](/doc/guide/basics.module). Маршрут к действию
контроллера внутри модуля задаётся в формате `moduleID/controllerID/actionID`.
Более подробно это описано в [разделе о модулях](/doc/guide/basics.module).

Создание экземпляра контроллера
-------------------------------
Экземпляр контроллера создаётся, когда [CWebApplication] обрабатывает входящий запрос.
Получив идентификатор контроллера, приложение использует следующие правила для
определения класса контроллера и его местоположения:

- если установлено свойство [CWebApplication::catchAllRequest], контроллер будет создан
на основе этого свойства, а контроллер, запрошенный пользователем, будет проигнорирован.
Как правило, это используется для установки приложения в режим технического обслуживания
и отображения статической страницы с соответствующим сообщением;

- если идентификатор контроллера обнаружен в [CWebApplication::controllerMap], то для
создания экземпляра контроллера  будет использована соответствующая конфигурация контроллера;

- если идентификатор контроллера соответствует формату `'path/to/xyz'`, то имя класса
контроллера определяется как `XyzController`, а соответствующий класс как
`protected/controllers/path/to/XyzController.php`.
Например, идентификатор контроллера `admin/user` будет соответствовать классу
контроллера — `UserController` и файлу `protected/controllers/admin/UserController.php`.
Если файл не существует, будет сгенерировано исключение [CHttpException] с кодом ошибки 404.

При использовании [модулей](/doc/guide/basics.module) процесс, описанный
выше, будет выглядеть несколько иначе. В частности, приложение проверит,
соответствует ли идентификатор контроллеру внутри модуля. Если соответствует, то
сначала будет создан экземпляр модуля, а затем экземпляр контроллера.


Действие
--------
Как было упомянуто выше, действие — это метод, имя которого начинается на `action`.
Более продвинутый способ — создать класс действия и указать контроллеру создавать
экземпляр этого класса при необходимости. Такой подход позволяет использовать
действия повторно.


Для создания класса действия необходимо выполнить следующее:

~~~
[php]
class UpdateAction extends CAction
{
	public function run()
	{
		// некоторая логика действия
	}
}
~~~

Чтобы контроллер знал об этом действии, необходимо переопределить метод
[actions()|CController::actions] в классе контроллера:

~~~
[php]
class PostController extends CController
{
	public function actions()
	{
		return array(
			'edit'=>'application.controllers.post.UpdateAction',
		);
	}
}
~~~

В приведённом коде мы используем псевдоним маршрута `application.controllers.post.UpdateAction`
для указания файла класса действия `protected/controllers/post/UpdateAction.php`.
Создавая действия, основанные на классах, можно организовать приложение в модульном стиле.
Например, следующая структура директорий может быть использована для организации кода контроллеров:
~~~
protected/
    controllers/
        PostController.php
        UserController.php
        post/
            CreateAction.php
            ReadAction.php
            UpdateAction.php
        user/
            CreateAction.php
            ListAction.php
            ProfileAction.php
            UpdateAction.php
~~~

### Привязка параметров действий

Начиная с версии 1.1.4, в Yii появилась поддержка автоматической привязки
параметров к действиям контроллера. То есть можно задать именованные
параметры, в которые автоматически будут попадать соответствующие значения из `$_GET`.

Для того чтобы показать, как это работает, предположим, что нам нужно
реализовать действие `create` контроллера `PostController`. Действие принимает
два параметра:

* `category`: ID категории, в которой будет создаваться запись (целое число);
* `language`: строка, содержащая код языка, который будет использоваться в записи.

Скорее всего, для получения параметров из `$_GET` в контроллере нам придётся написать следующий скучный код:

~~~
[php]
class PostController extends CController
{
	public function actionCreate()
	{
		if(isset($_GET['category']))
			$category=(int)$_GET['category'];
		else
			throw new CHttpException(404,'неверный запрос');

		if(isset($_GET['language']))
			$language=$_GET['language'];
		else
			$language='en';

		// … действительно полезная часть кода …
	}
}
~~~

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

~~~
[php]
class PostController extends CController
{
	public function actionCreate($category, $language='en')
	{
	    $category=(int)$category;

		// … действительно полезная часть кода …
	}
}
~~~

Мы добавляем два параметра методу `actionCreate`. Имя каждого должно в точности
совпадать с одним из ключей в `$_GET`. Параметру `$language` задано значение
по умолчанию `en`, которое используется, если в запросе соответствующий параметр
отсутствует. Так как `$category` не имеет значения по умолчанию, в случае
отсутствия соответствующего параметра в запросе будет автоматически выброшено
исключение [CHttpException] (с кодом ошибки 400).

Начиная с версии 1.1.5, Yii поддерживает указание массивов в качестве параметров действий.
Использовать их можно следующим образом:

~~~
[php]
class PostController extends CController
{
	public function actionCreate(array $categories)
	{
		// Yii приведёт $categories к массиву
	}
}
~~~

Мы добавляем ключевое слово `array` перед параметром `$categories`.
В результате, если параметр `$_GET['categories']` является простой строкой, то он будет
приведён к массиву, содержащему исходную строку.

> Note|Примечание: Если параметр объявлен без указания типа `array`, то он должен
> быть скалярным (т.е. не массивом). В этом случае передача массива через
> `$_GET` параметр приведёт к исключению HTTP.


Начиная с версии 1.1.7, автоматическая привязка параметров работает и с
действиями, оформленными в виде классов. Если метод `run()` в классе действия
описать с параметрами, то эти параметры наполняются соответствующими значениями
из HTTP-запроса:

~~~
[php]
class UpdateAction extends CAction
{
	public function run($id)
	{
		// $id будет заполнен значением из $_GET['id']
	}
}
~~~

Фильтры
-------
Фильтр — это часть кода, которая может выполняться до или после
выполнения действия контроллера в зависимости от конфигурации. Например, фильтр
контроля доступа может проверять, аутентифицирован ли пользователь перед тем,
как будет выполнено запрошенное действие. Фильтр, контролирующий производительность,
может быть использован для определения времени, затраченного на выполнение действия.

Действие может иметь множество фильтров. Фильтры запускаются в том порядке, в котором
они указаны в списке фильтров, при этом фильтр может предотвратить выполнение
действия и следующих за ним фильтров.

Фильтр может быть определён как метод класса контроллера. Имя метода должно начинаться на `filter`.
Например, метод `filterAccessControl` определяет фильтр `accessControl`.
Метод фильтра должен выглядеть так:

~~~
[php]
public function filterAccessControl($filterChain)
{
	// для выполнения последующих фильтров и выполнения действия вызовите метод $filterChain->run()
}
~~~

где `$filterChain` — экземпляр класса [CFilterChain], представляющего собой список
фильтров, ассоциированных с запрошенным действием. В коде фильтра можно вызвать
`$filterChain->run()` для того, чтобы продолжить выполнение последующих фильтров и действия.

Фильтр также может быть экземпляром класса [CFilter] или его производного.
Следующий код определяет новый класс фильтра:

~~~
[php]
class PerformanceFilter extends CFilter
{
	protected function preFilter($filterChain)
	{
		// код, выполняемый до выполнения действия
		return true; // false — для случая, когда действие не должно быть выполнено
	}

	protected function postFilter($filterChain)
	{
		// код, выполняемый после выполнения действия
	}
}
~~~

Для того чтобы применить фильтр к действию, необходимо переопределить метод
`CController::filters()`, возвращающий  массив конфигураций фильтров. Например:

~~~
[php]
class PostController extends CController
{
	…
	public function filters()
	{
		return array(
			'postOnly + edit, create',
			array(
				'application.filters.PerformanceFilter - edit, create',
				'unit'=>'second',
			),
		);
	}
}
~~~

Данный код определяет два фильтра: `postOnly` и `PerformanceFilter`.
Фильтр `postOnly` задан как метод (соответствующий метод уже определен в
[CController]), в то время как `PerformanceFilter` — фильтр на базе класса.
Псевдоним `application.filters.PerformanceFilter` указывает на файл класса фильтра —
`protected/filters/PerformanceFilter`. Для конфигурации `PerformanceFilter`
используется массив, что позволяет задать начальные значения свойств фильтра.
В данном случае свойство `unit` фильтра `PerformanceFilter` будет
инициализировано значением `'second'`.

Используя операторы `'+'` и `'-'` можно указать, к каким действиям должен и
не должен быть применён фильтр. В приведённом примере `postOnly` будет
применён к действиям `edit` и `create`, а `PerformanceFilter` — ко всем действиям,
*кроме* `edit` и `create`. Если операторы `'+'` и `'-'` не указаны, фильтр будет
применён ко всем действиям.