You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
29 KiB
415 lines
29 KiB
10 years ago
|
Контролери
|
||
|
===========
|
||
|
|
||
|
Контролери є частиною [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`:
|
||
|
|
||
|
```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`, через яке користувач зможе вказати необхідні дані.
|
||
|
|
||
|
|
||
|
## Маршрути <a name="routes"></a>
|
||
|
|
||
|
Кінцеві користувачі звертаються до подій з допомогою так названих *маршрутів*. Маршрут це рядок, який складається з наступних частин:
|
||
|
|
||
|
* ID модуля: він існує, тільки якщо контролер належить не додатку, а [модулю](structure-modules.md);
|
||
|
* ID контролера: рядок, який унікально ідентифікує контролер серед всіх інших контролерів одного і того ж додатка
|
||
|
(або одного й того ж модуля, якщо контролер належить модулю);
|
||
|
* ID події: рядок, який унікально идентифікує подію серед всіх інших подій одного й того ж конторолера.
|
||
|
|
||
|
Маршрути можуть мати наступний формат:
|
||
|
|
||
|
```
|
||
|
ControllerID/ActionID
|
||
|
```
|
||
|
|
||
|
або наступний формат, якщо контролер належить модулю:
|
||
|
|
||
|
```php
|
||
|
ModuleID/ControllerID/ActionID
|
||
|
```
|
||
|
|
||
|
Таким чином, якщо користувач звертається до URL `http://hostname/index.php?r=site/index`, то `index` подія в `site` контролері яку буде викликано.
|
||
|
Секція [Маршрутизація](runtime-routing.md) містить більш детальну інформацію про те як маршрути співставляються з подіями.
|
||
|
|
||
|
|
||
|
## Створення контролерів <a name="creating-controllers"></a>
|
||
|
|
||
|
В [[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 контролерів <a name="controller-ids"></a>
|
||
|
|
||
|
За звичай контролер зроблений таким чином, що він мусить опрацьовувати запити, які пов’язані з певним ресурсом.
|
||
|
Саме через дані причини, ID контролерів за завичай є іменниками, які посилаються на ресурс, який вони опрацьовують.
|
||
|
Наприклад, ви можете використовувати `article` в якості ID контролера, який відповідає за опрацювання даних публікацій.
|
||
|
|
||
|
За замовчуванням, ID контролерів мають містити тільки наступні символи: Англійські букви в нижньому регістрі, цифри, підкреслення,
|
||
|
тире и слеш. Наприклад, обидва `article` і `post-comment` є прийнятними ID контролерів, в той час як `article?`, `PostComment`,
|
||
|
`admin\post` не являються такими.
|
||
|
|
||
|
ID контролерів також можуть містити префікс субдиректорії. Наприклад, в `admin/article` частина `article` є контролером в
|
||
|
субдиректорії `admin` в [[yii\base\Application::controllerNamespace|порсторі імен]].
|
||
|
Допустимими символами для префіксів субдиректорій є: Англійські букви в нижньому і верхньому регістрі, символи підкреслення і слеш, де слеш використовується в якості розділителя для багатовкладених субдиректорій (наприклад `panels/admin`).
|
||
|
|
||
|
|
||
|
|
||
|
### Правила найменування класів контролерів <a name="controller-class-naming"></a>
|
||
|
|
||
|
Назви класів контролерів можуть бути отримані з 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).
|
||
|
|
||
|
|
||
|
### Мапа контролерів <a name="controller-map"></a>
|
||
|
|
||
|
Ви можете налаштувати [[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,
|
||
|
],
|
||
|
],
|
||
|
],
|
||
|
]
|
||
|
```
|
||
|
|
||
|
### Контролер за замовчуванням <a name="default-controller"></a>
|
||
|
|
||
|
Кожний додаток має контролер за замовчуванням, вказаний через властивість [[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
|
||
|
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 подій <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
|
||
|
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]] або його потомків, і реалізувати
|
||
|
метод `run()` з областю видимості public. Роль метода `run()` аналогічна іншим методам подій. Наприклад,
|
||
|
|
||
|
```php
|
||
|
<?php
|
||
|
namespace app\components;
|
||
|
|
||
|
use yii\base\Action;
|
||
|
|
||
|
class HelloWorldAction extends Action
|
||
|
{
|
||
|
public function run()
|
||
|
{
|
||
|
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
|
||
|
public function actionForward()
|
||
|
{
|
||
|
// перенаправляємо браузер користувача на 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
|
||
|
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).
|
||
|
|
||
|
|
||
|
### Події за замовчуванням <a name="default-action"></a>
|
||
|
|
||
|
Кожний контролер містить події, визначені через властивість [[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');
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
## Життєвий цикл контролера <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).
|