Контролери
Контролери є частиною MVC архітектури. Це об’єкти класів, успадкованих від [[yii\base\Controller]] та відповідають за обработку запитів і генерацію відповідей. Зокрема, після отримання контролю від додатків, контролери проаналізують вхідні дані, передадуть їх у моделі, додадуть результати моделі у представлення, і в кінцевому підсумку згенерують вихідні відповіді.
Дії
Контролери складаються з дій, які є основними блоками, до яких може звертатись кінцевий користувач і запитувати виконання того або іншого функціоналу. В контролері може бути одна або декілька дій.
Наступний приклад показує post
контролер з двома діями: view
та create
:
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()
) код спочатку завантажує модель,
відповідно запитуваної ID моделі; Якщо модель успішно завантажена, то код відобразить її за допомогою
представлення, під назвою view
. В іншому випадку - буде визване виключення.
У дії create
(визначеній методом actionCreate()
), код аналогічний. Він спочатку намагається завантажити
модель за допомогою даних із запиту і зберегти модель. Якщо все пройшло успішно,
то код перенаправить браузер на дію view
із ID щойно створеної моделі. В іншому випадку - він відобразить
предаставлення create
, через яке користувач зможе вказати необхідні дані.
Маршрути
Кінцеві користувачі звертаються до дій з допомогою так названих маршрутів. Маршрут це рядок, який складається з наступних частин:
- ID модуля: він існує, тільки якщо контролер належить не додатку, а модулю;
- ID контролера: рядок, який унікально ідентифікує контролер серед всіх інших контролерів одного і того ж додатка (або одного й того ж модуля, якщо контролер належить модулю);
- ID дії: рядок, який унікально ідентифікує дію серед всіх інших дій одного й того ж конторолера.
Маршрути можуть мати наступний формат:
ControllerID/ActionID
або наступний формат, якщо контролер належить модулю:
ModuleID/ControllerID/ActionID
Таким чином, якщо користувач звертається до URL http://hostname/index.php?r=site/index
, то буде викликано дію index
у контролері site
. Розділ Маршрутизація та створення URL містить більш детальну інформацію
про те, як маршрути співставляються із діями.
Створення контролерів
У [[yii\web\Application|веб додатках]] контролери повинні бути успадкованими від класу [[yii\web\Controller]]
або його нащадків. Аналогічно для [[yii\console\Application|консольних додатків]], контролери повинні бути
успадкованими від класу [[yii\console\Controller]] або його нащадків. Наступний код визначає контролер site
:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
}
ID контролерів
За звичай контролер зроблений таким чином, що він повинен обробляти запити, які пов’язані з певним ресурсом.
Саме з цієї причини, ID контролерів за завичай є іменниками, які посилаються на ресурс, який вони обробляють.
Наприклад, ви можете використовувати article
в якості ID контролера, який відповідає за обробку даних публікацій.
За замовчуванням, ID контролерів мають містити тільки наступні символи: англійські букви в нижньому регістрі, цифри,
підкреслення, тире і слеш. Наприклад, обидва article
та post-comment
є прийнятними ID контролерів, в той час,
як article?
, PostComment
, admin\post
не є такими.
ID контролера також може містити префікс субдиректорії. Наприклад, у admin/article
- частина article
відповідає
контролеру в субдиректорії admin
[[yii\base\Application::controllerNamespace|простору імен контролера]].
Допустимими символами для префіксів субдиректорій є: англійські букви в нижньому і верхньому регістрах, цифри,
символи підкреслення і слеш, де слеш - використовується в якості роздільника для багаторівневих субдиректорій
(наприклад panels/admin
).
Іменування класів контролерів
Назви класів контролерів можуть бути отримані із 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
.
Класи контролерів мають бути автозавантаженими. Саме з цієї причини у вищенаведених прикладах
контролер article
має бути збереженим у файл, псевдонім шляху якого є
@app/controllers/ArticleController.php
; в той час, як контролер admin/post2-comment
має знаходитись у файлі
@app/controllers/admin/Post2CommentController.php
.
Інформація: Останній приклад
admin/post2-comment
показує яким чином ви можете розташувати контролер в директорії [[yii\base\Application::controllerNamespace|простору імен контролера]]. Це дуже зручно, коли ви хочете організувати свої контролери у декілька категорій і не хочете використовувати модулі.
Мапа контролерів
Ви можете налаштувати [[yii\base\Application::controllerMap|мапу контролерів]] для того, щоб подолати описані вище обмеження іменування ID контролерів і назв класів. В основному, це дуже зручно, коли ви використовуєте сторонні контролери, іменування яких ви не можете контролювати.
Ви можете налаштувати [[yii\base\Application::controllerMap|мапу контролерів]] в налаштуваннях додатка наступним чином:
[
'controllerMap' => [
// оголошує контролер "account", використовуючи назву класу
'account' => 'app\controllers\UserController',
// оголошує контролер "article", використовуючи масив конфігурації
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]
Контролер за замовчуванням
Кожний додаток має контролер за замовчуванням, вказаний через властивість [[yii\base\Application::defaultRoute]].
Коли в запиті не вказано маршрут, то буде використано маршрут із даної властивості.
Для [[yii\web\Application|веб додатків]], це значення рівне 'site'
, у той час, як для
[[yii\console\Application|консольних додатків]], це 'help'
. Таким чином, якщо вказаний URL
http://hostname/index.php
, це значить, що контролер site
виконає обробку запиту.
Ви можете змінити контролер за замовчуванням наступним чином в налаштуваннях додатку:
[
'defaultRoute' => 'main',
]
Створення дій
Створення дій може бути таким же простим, як і оголошення так званих методов дій у класі контролера.
Метод дії це public метод, ім’я якого починається зі слова action
. Значення методі дії, що повертається, є даними
відповіді, які будуть вислані кінцевому користувачу. Наведений нижче код визначає дві дії - index
і hello-world
:
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 дій
Частіше за все, дія розробляється для певної конкретної обробки ресурса. З цієї причини ID дій, в основному,
є дієсловами, такими як view
, update
, і т.д.
За замовчуванням, ID дій повинні містити тільки такі символи: англійські букви в нижньому регістрі, цифри,
підкреслення і дефіси. Дефіси в ID дій використовуються для поділу слів. Наприклад, view
, update2
,
comment-post
є допустимими ID дій, в той час, як view?
, Update
не є такими.
Ви можете створювати дії двома способами: вбудовані дії і окремі дії. Вбудована дія є методом, визначеним в класі контролера, тоді як окрема дія є екземпляром класу, успадкованого від [[yii\base\Action]] або його нащадків. Вбудовані дії вимагають менше зусиль для створення і, в основному, використовуються якщо у вас немає потреби у повторному використанні цих дій. Окремі дії, з іншого боку, в основному створюються для використання в різних контролерах або при використанні у розширеннях.
Вбудовані дії
Вбудовані дії це ті дії, які визначені у рамках методів контролера, як ми це вже обговорили.
Назви методів дій можуть бути отримані із ID дій наступним чином:
- Привести перший символ кожного слова в ID дії у верхній регістр;
- Прибрати дефіси;
- Додати префікс
action
.
Наприклад, index
перетвориться у actionIndex
, а hello-world
перетвориться у actionHelloWorld
.
Примітка: Назви імен дій є регістрозалежними. Якщо у вас є метод
ActionIndex
, то його не буде враховано як метод дії, і в результаті, запит до діїindex
призведе до отримання виключення. Також слід врахувати, що методи дій повинні бути публічними ("public"). Приватні ("private") або захищені ("protected") методи НЕ визначають вбудованих дій.
В основному використовуються вбудовані дії, оскільки для їх створення не потрібного багато зусиль. Тим не менше, якщо ви плануєте повторно використовувати деякі дії у різних місцях або якщо ви хочете перерозподілити дії, ви повинні визначити їх як окремими діями.
Окремі дії
Окремі дії визначаються в якості класів, успадкованих від [[yii\base\Action]] або його нащадків. Наприклад, в релізах Yii присутні [[yii\web\ViewAction]] та [[yii\web\ErrorAction]], обидва класи є окремими діями.
Для використання окремої дії, ви маєте вказати її у мапі дій за допомогою перевизначення методу [[yii\base\Controller::actions()]] у вашому класі контролера, наступним чином:
public function actions()
{
return [
// оголошує дію "error" за допомогою назви класу
'error' => 'yii\web\ErrorAction',
// оголошує дію "view" за допомогою конфігураційного масиву
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
],
];
}
Як ви можете бачити, метод actions()
повинен повернути масив, ключами якого є ID дій, а значеннями - відповідні
назви класів дій або конфігурацій. На відміну від вбудованих дій, ID окремих дій
можуть містити довільні символи, доки вони визначені у методі actions()
.
Для створення окремої дії, ви повинні успадкуватись від класу [[yii\base\Action]] або його нащадків, і реалізувати
публічний ("public") метод run()
. Роль метода run()
аналогічна іншим методам дій. Наприклад,
<?php
namespace app\components;
use yii\base\Action;
class HelloWorldAction extends Action
{
public function run()
{
return "Hello World";
}
}
Результати дій
Значення, що повертається від метода дії або метода run()
окремої дії дуже важливе.
Воно є результатом виконання відповідної дії.
Значення, що повертається, може бути об’єктом відповіді, яке буде відправлено кінцевому користувачу.
- Для [[yii\web\Application|веб додатків]], значення, що повертається, також може бути довільними даними, яке буде призначене до [[yii\web\Response::data]], а потім конвертоване у рядок, що представляє тіло відповіді.
- Для [[yii\console\Application|консольних додатків]], значення, що повертається, також може бути числом, що представляє [[yii\console\Response::exitStatus|статус виходу]] виконання команди.
В вищенаведених прикладах всі результати дій є рядками, які будуть використані у якості тіла відповіді користувачу. Наступний приклад показує як дія може перенаправити браузер користувача на новий URL за допомогою повернення об’єкта відповіді (оскільки метод [[yii\web\Controller::redirect()|redirect()]] повертає об’єкт response):
public function actionForward()
{
// перенаправляємо браузер користувача на http://example.com
return $this->redirect('http://example.com');
}
Параметри дій
Методи дій для вбудованих дій і методи run()
для окремих дій можуть приймати, так звані, параметри дії.
Їх значення беруться із запитів. Для [[yii\web\Application|веб додатків]], значення кожного з параметрів дії
береться із $_GET
, використовуючи назву параметра у якості ключа;
для [[yii\console\Application|консольних додатків]] - вони відповідають аргументам командної строки.
В наступному прикладі, дія view
(вбудовона дія) визначає два параметри: $id
і $version
.
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
параметра метода,
як зображено нажче:
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'
буде автоматично сконвертовано у масив.
Вищенаведені приклади в основному показують як параметри дій працюють для веб додатків. Більше інформації про параметри консольних додатків наведено в секції Консольні команди.
Дія за замовчуванням
Кожний контролер містить дію за замовчуванням, визначену через властивість [[yii\base\Controller::defaultAction]]. Коли маршрут містить тільки ID контролера, то розуміється, що було запитана дія контролера за замовчуванням.
За замовчуванням, ця дія має значення index
. Якщо ви хочете змінити це значення - просто перевизначте дану
властивість у класі контролера наступним чином:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}
Життєвий цикл контролера
При обробці запиту, додаток створить контролер, базуючись на маршруті, який було запитано. Для виконання запиту, контролер пройде через наступні етапи життєвого циклу:
- Метод [[yii\base\Controller::init()]] буде викликаний після того, як контролер був створений і сконфігурований.
- Контролер створить об’єкт дії, базуючись на ID дії, яку було запитано:
- Якщо ID дії не вказано, то буде використано [[yii\base\Controller::defaultAction|ID дії за замовчуванням]];
- Якщо ID дії знайдено у [[yii\base\Controller::actions()|мапі дій]], то буде створено окрему дію;
- Якщо ID дії відповідає методу дії, то буде створено вбудовану дію;
- В іншому випадку, буде отримане виключення [[yii\base\InvalidRouteException]].
- Контролер послідовно викликає метод
beforeAction()
додатка, модуля (якщо контролер належить модулю) і самого контролера.- Якщо один із методів повернув
false
, то решта невикликаних методівbeforeAction
будуть пропущені, а виконання дії буде відмінено; - За замовчуванням, кожний виклик метода
beforeAction()
викликає подіюbeforeAction
, на яку ви можете призначити обробники.
- Якщо один із методів повернув
- Контролер виконує дію:
- Параметри дії будуть проаналізовані і заповнені із даних запиту.
- Контролер послідовно викликає методи
afterAction
контролера, модуля (якщо контролер належить модулю) і додатка.- За замовчуванням, кожний виклик метода
afterAction()
викликає подіюafterAction
, на яку ви можете призначити обробники.
- За замовчуванням, кожний виклик метода
- Додаток, отримавши результат виконання дії, привласнює його об’єкту response.
Кращі практики
В добре організованих додатках, контролери за звичай дуже тонкі, і містять лише декілька рядків коду. Якщо ваш контролер дуже складний, це за звичай означає, що вам потрібно провести його рефакторинг і перенести деякий код в інші класи.
В цілому, контролери
- можуть мати доступ до даних запиту;
- можуть викликати методи моделей та інших компонентів системи із даними запиту;
- можуть використовувати представлення для формування відповіді;
- не повинні займатись обробкою даних - це має відбуватися у моделях;
- мають уникати використання HTML або іншої розмітки - краще це робити у представленнях.