Files
yii2/docs/guide-uk/structure-controllers.md
2015-01-25 22:13:16 +01:00

449 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Контролери
==========
Контролери є частиною [MVC](http://uk.wikipedia.org/wiki/Модель-вид-контролер) архітектури. Це об’єкти класів,
успадкованих від [[yii\base\Controller]] та відповідають за обработку запитів і генерацію відповідей.
Зокрема, після отримання контролю від [додатків](structure-applications.md), контролери проаналізують
вхідні дані, передадуть їх у [моделі](structure-models.md), додадуть результати моделі у
[представлення](structure-views.md), і в кінцевому підсумку згенерують вихідні відповіді.
## Дії <span id="actions"></span>
Контролери складаються з *дій*, які є основними блоками, до яких може звертатись кінцевий користувач і запитувати
виконання того або іншого функціоналу. В контролері може бути одна або декілька дій.
Наступний приклад показує `post` контролер з двома діями: `view` та `create`:
```php
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
return $this->render('view', [
'model' => $model,
]);
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
```
У дії `view` (визначеній методом `actionView()`) код спочатку завантажує [модель](structure-models.md),
відповідно запитуваної ID моделі; Якщо модель успішно завантажена, то код відобразить її за допомогою
[представлення](structure-views.md), під назвою `view`. В іншому випадку - буде визване виключення.
У дії `create` (визначеній методом `actionCreate()`), код аналогічний. Він спочатку намагається завантажити
[модель](structure-models.md) за допомогою даних із запиту і зберегти модель. Якщо все пройшло успішно,
то код перенаправить браузер на дію `view` із ID щойно створеної моделі. В іншому випадку - він відобразить
предаставлення `create`, через яке користувач зможе вказати необхідні дані.
## Маршрути <span id="routes"></span>
Кінцеві користувачі звертаються до дій з допомогою так названих *маршрутів*. Маршрут це рядок,
який складається з наступних частин:
* ID модуля: він існує, тільки якщо контролер належить не додатку, а [модулю](structure-modules.md);
* ID контролера: рядок, який унікально ідентифікує контролер серед всіх інших контролерів одного і того ж додатка
(або одного й того ж модуля, якщо контролер належить модулю);
* ID дії: рядок, який унікально ідентифікує дію серед всіх інших дій одного й того ж конторолера.
Маршрути можуть мати наступний формат:
```
ControllerID/ActionID
```
або наступний формат, якщо контролер належить модулю:
```php
ModuleID/ControllerID/ActionID
```
Таким чином, якщо користувач звертається до URL `http://hostname/index.php?r=site/index`, то буде викликано дію `index`
у контролері `site`. Розділ [Маршрутизація та створення URL](runtime-routing.md) містить більш детальну інформацію
про те, як маршрути співставляються із діями.
## Створення контролерів <span id="creating-controllers"></span>
У [[yii\web\Application|веб додатках]] контролери повинні бути успадкованими від класу [[yii\web\Controller]]
або його нащадків. Аналогічно для [[yii\console\Application|консольних додатків]], контролери повинні бути
успадкованими від класу [[yii\console\Controller]] або його нащадків. Наступний код визначає контролер `site`:
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
}
```
### ID контролерів <span id="controller-ids"></span>
За звичай контролер зроблений таким чином, що він повинен обробляти запити, які пов’язані з певним ресурсом.
Саме з цієї причини, ID контролерів за завичай є іменниками, які посилаються на ресурс, який вони обробляють.
Наприклад, ви можете використовувати `article` в якості ID контролера, який відповідає за обробку даних публікацій.
За замовчуванням, ID контролерів мають містити тільки наступні символи: англійські букви в нижньому регістрі, цифри,
підкреслення, тире і слеш. Наприклад, обидва `article` та `post-comment` є прийнятними ID контролерів, в той час,
як `article?`, `PostComment`, `admin\post` не є такими.
ID контролера також може містити префікс субдиректорії. Наприклад, у `admin/article` - частина `article` відповідає
контролеру в субдиректорії `admin` [[yii\base\Application::controllerNamespace|простору імен контролера]].
Допустимими символами для префіксів субдиректорій є: англійські букви в нижньому і верхньому регістрах, цифри,
символи підкреслення і слеш, де слеш - використовується в якості роздільника для багаторівневих субдиректорій
(наприклад `panels/admin`).
### Іменування класів контролерів <span id="controller-class-naming"></span>
Назви класів контролерів можуть бути отримані із ID контролерів наступними правилами:
* Привести у верхній регістр перший символ в кожному слові, розділеному дефісами. Зверніть увагу, що, якщо
ID контролера містить слеш, то дане правило поширюється тільки на частину після останнього слеша в ID контролера.
* Прибрати дефіси і замінити будь-який прямий слеш на зворотний.
* Додати суфікс `Controller`.
* Додати на початок [[yii\base\Application::controllerNamespace|простір імен контролера]].
Нижче наведено декілька прикладів, з урахуванням того, що
[[yii\base\Application::controllerNamespace|простір імен контролера]] має значення за замовчуванням `app\controllers`:
* `article` відповідає `app\controllers\ArticleController`;
* `post-comment` відповідає `app\controllers\PostCommentController`;
* `admin/post-comment` відповідає `app\controllers\admin\PostCommentController`;
* `adminPanels/post-comment` відповідає `app\controllers\adminPanels\PostCommentController`.
Класи контролерів мають бути [автозавантаженими](concept-autoloading.md). Саме з цієї причини у вищенаведених прикладах
контролер `article` має бути збереженим у файл, [псевдонім шляху](concept-aliases.md) якого є
`@app/controllers/ArticleController.php`; в той час, як контролер `admin/post2-comment` має знаходитись у файлі
`@app/controllers/admin/Post2CommentController.php`.
> Інформація: Останній приклад `admin/post2-comment` показує яким чином ви можете розташувати контролер в директорії
[[yii\base\Application::controllerNamespace|простору імен контролера]]. Це дуже зручно, коли ви хочете організувати
свої контролери у декілька категорій і не хочете використовувати [модулі](structure-modules.md).
### Мапа контролерів <span id="controller-map"></span>
Ви можете налаштувати [[yii\base\Application::controllerMap|мапу контролерів]] для того, щоб подолати
описані вище обмеження іменування ID контролерів і назв класів. В основному, це дуже зручно, коли ви використовуєте
сторонні контролери, іменування яких ви не можете контролювати.
Ви можете налаштувати [[yii\base\Application::controllerMap|мапу контролерів]] в
[налаштуваннях додатка](structure-applications.md#application-configurations) наступним чином:
```php
[
'controllerMap' => [
// оголошує контролер "account", використовуючи назву класу
'account' => 'app\controllers\UserController',
// оголошує контролер "article", використовуючи масив конфігурації
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]
```
### Контролер за замовчуванням <span id="default-controller"></span>
Кожний додаток має контролер за замовчуванням, вказаний через властивість [[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',
]
```
## Створення дій <span id="creating-actions"></span>
Створення дій може бути таким же простим, як і оголошення так званих *методов дій* у класі контролера.
Метод дії це *public* метод, ім’я якого починається зі слова `action`. Значення методі дії, що повертається, є даними
відповіді, які будуть вислані кінцевому користувачу. Наведений нижче код визначає дві дії - `index` і `hello-world`:
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionHelloWorld()
{
return 'Hello World';
}
}
```
### ID дій <span id="action-ids"></span>
Частіше за все, дія розробляється для певної конкретної обробки ресурса. З цієї причини ID дій, в основному,
є дієсловами, такими як `view`, `update`, і т.д.
За замовчуванням, ID дій повинні містити тільки такі символи: англійські букви в нижньому регістрі, цифри,
підкреслення і дефіси. Дефіси в ID дій використовуються для поділу слів. Наприклад, `view`, `update2`,
`comment-post` є допустимими ID дій, в той час, як `view?`, `Update` не є такими.
Ви можете створювати дії двома способами: вбудовані дії і окремі дії. Вбудована дія є методом, визначеним
в класі контролера, тоді як окрема дія є екземпляром класу, успадкованого від [[yii\base\Action]] або його нащадків.
Вбудовані дії вимагають менше зусиль для створення і, в основному, використовуються якщо у вас немає потреби
у повторному використанні цих дій. Окремі дії, з іншого боку, в основному створюються для використання в різних
контролерах або при використанні у [розширеннях](structure-extensions.md).
### Вбудовані дії <span id="inline-actions"></span>
Вбудовані дії це ті дії, які визначені у рамках методів контролера, як ми це вже обговорили.
Назви методів дій можуть бути отримані із ID дій наступним чином:
* Привести перший символ кожного слова в ID дії у верхній регістр;
* Прибрати дефіси;
* Додати префікс `action`.
Наприклад, `index` перетвориться у `actionIndex`, а `hello-world` перетвориться у `actionHelloWorld`.
> Примітка: Назви імен дій є *регістрозалежними*. Якщо у вас є метод `ActionIndex`, то його не буде враховано
як метод дії, і в результаті, запит до дії `index` призведе до отримання виключення. Також слід врахувати,
що методи дій повинні бути публічними ("public"). Приватні ("private") або захищені ("protected") методи
НЕ визначають вбудованих дій.
В основному використовуються вбудовані дії, оскільки для їх створення не потрібного багато зусиль.
Тим не менше, якщо ви плануєте повторно використовувати деякі дії у різних місцях або якщо ви хочете
перерозподілити дії, ви повинні визначити їх як *окремими діями*.
### Окремі дії <span id="standalone-actions"></span>
Окремі дії визначаються в якості класів, успадкованих від [[yii\base\Action]] або його нащадків.
Наприклад, в релізах Yii присутні [[yii\web\ViewAction]] та [[yii\web\ErrorAction]], обидва класи є окремими діями.
Для використання окремої дії, ви маєте вказати її у *мапі дій* за допомогою перевизначення методу
[[yii\base\Controller::actions()]] у вашому класі контролера, наступним чином:
```php
public function actions()
{
return [
// оголошує дію "error" за допомогою назви класу
'error' => 'yii\web\ErrorAction',
// оголошує дію "view" за допомогою конфігураційного масиву
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
],
];
}
```
Як ви можете бачити, метод `actions()` повинен повернути масив, ключами якого є ID дій, а значеннями - відповідні
назви класів дій або [конфігурацій](concept-configurations.md). На відміну від вбудованих дій, ID окремих дій
можуть містити довільні символи, доки вони визначені у методі `actions()`.
Для створення окремої дії, ви повинні успадкуватись від класу [[yii\base\Action]] або його нащадків, і реалізувати
публічний ("public") метод `run()`. Роль метода `run()` аналогічна іншим методам дій. Наприклад,
```php
<?php
namespace app\components;
use yii\base\Action;
class HelloWorldAction extends Action
{
public function run()
{
return "Hello World";
}
}
```
### Результати дій <span id="action-results"></span>
Значення, що повертається від метода дії або метода `run()` окремої дії дуже важливе.
Воно є результатом виконання відповідної дії.
Значення, що повертається, може бути об’єктом [відповіді](runtime-responses.md), яке буде відправлено
кінцевому користувачу.
* Для [[yii\web\Application|веб додатків]], значення, що повертається, також може бути довільними даними,
яке буде призначене до [[yii\web\Response::data]], а потім конвертоване у рядок, що представляє тіло відповіді.
* Для [[yii\console\Application|консольних додатків]], значення, що повертається, також може бути числом, що
представляє [[yii\console\Response::exitStatus|статус виходу]] виконання команди.
В вищенаведених прикладах всі результати дій є рядками, які будуть використані у якості тіла відповіді користувачу.
Наступний приклад показує як дія може перенаправити браузер користувача на новий URL за допомогою
повернення об’єкта відповіді (оскільки метод [[yii\web\Controller::redirect()|redirect()]] повертає об’єкт response):
```php
public function actionForward()
{
// перенаправляємо браузер користувача на http://example.com
return $this->redirect('http://example.com');
}
```
### Параметри дій <span id="action-parameters"></span>
Методи дій для вбудованих дій і методи `run()` для окремих дій можуть приймати, так звані, *параметри дії*.
Їх значення беруться із запитів. Для [[yii\web\Application|веб додатків]], значення кожного з параметрів дії
береться із `$_GET`, використовуючи назву параметра у якості ключа;
для [[yii\console\Application|консольних додатків]] - вони відповідають аргументам командної строки.
В наступному прикладі, дія `view` (вбудовона дія) визначає два параметри: `$id` і `$version`.
```php
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public function actionView($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
public function actionView(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).
### Дія за замовчуванням <span id="default-action"></span>
Кожний контролер містить дію за замовчуванням, визначену через властивість [[yii\base\Controller::defaultAction]].
Коли [маршрут](#ids-routes) містить тільки ID контролера, то розуміється, що було запитана дія контролера
за замовчуванням.
За замовчуванням, ця дія має значення `index`. Якщо ви хочете змінити це значення - просто перевизначте дану
властивість у класі контролера наступним чином:
```php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}
```
## Життєвий цикл контролера <span id="controller-lifecycle"></span>
При обробці запиту, [додаток](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).
## Кращі практики <span id="best-practices"></span>
В добре організованих додатках, контролери за звичай дуже тонкі, і містять лише декілька рядків коду.
Якщо ваш контролер дуже складний, це за звичай означає, що вам потрібно провести його рефакторинг і
перенести деякий код в інші класи.
В цілому, контролери
* можуть мати доступ до даних [запиту](runtime-requests.md);
* можуть викликати методи [моделей](structure-models.md) та інших компонентів системи із даними запиту;
* можуть використовувати [представлення](structure-views.md) для формування відповіді;
* не повинні займатись обробкою даних - це має відбуватися у [моделях](structure-models.md);
* мають уникати використання HTML або іншої розмітки - краще це робити у [представленнях](structure-views.md).