Контролер
=========
`Контролер (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) – це частина коду, що може виконуватися до або після
виконання дії контролера залежно від конфігурації. Наприклад, фільтр
контролю доступу може перевіряти, чи аутентифікований користувач перед тим,
як буде виконана запитана дія. Фільтр, що контролює продуктивність додатку,
може бути використаний для визначення часу, витраченого на виконання дії.

Дія може мати безліч фільтрів. Фільтри запускаються в такому порядку, як
вони зазначені в списку фільтрів, при цьому фільтр може запобігти виконанню
дії і наступних за ним фільтрів.

Фільтр може бути визначений як метод класу контролера. Імʼя методу повинне починатися на `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`. Якщо оператори `'+'` і `'-'` не зазначені, фільтр буде
застосований до всіх дій.