Mark
10 years ago
1 changed files with 440 additions and 0 deletions
@ -0,0 +1,440 @@
|
||||
Контроллеры |
||||
=========== |
||||
|
||||
Контроллеры являются частью [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). |
Loading…
Reference in new issue