diff --git a/docs/guide-ru/structure-controllers.md b/docs/guide-ru/structure-controllers.md new file mode 100644 index 0000000..9733dfa --- /dev/null +++ b/docs/guide-ru/structure-controllers.md @@ -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), +и в конечном итоге сгенерируют исходящие ответы. + + +## Действия + +Контроллеры состоят из *действий*, которые являются основными блоками, к которым может обращаться конечный пользователь и запрашивать исполнение того или иного +функционала. В контроллере может быть одно или несколько действий. + +Следующий пример показывает `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`, через которое пользователь может заполнить нужные данные. + + +## Маршруты + +Конечные пользователи обращаются к действиям через так называемые *маршурты*. Маршрут это строка, состоящая из следующих частей: + +* 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) содержит более подробную информацию о том как маршруты сопоставляются с действиями. + + +## Создание контроллеров + +В [[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 контроллеров + +Обычно контроллер сделан таким образом, что он должен обрабатывать запросы, связанные с определенным ресурсом. +Именно по этим причинам, 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`. + +Классы контроллеров должны быть [автозагружаемыми](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). + + +### Карта контроллеров + +Вы можете сконфигурировать [[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, + ], + ], + ], +] +``` + +### Контроллер по-умолчанию + +Каждое приложение имеет контроллер по-умолчанию, указанный через свойство [[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', +] +``` + + +## Создание действий + +Создание действий не представляет сложностей также как и объявление так называемых *методов действий* в классе контроллера. Метод действия +это *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 действий + +В основном действие разрабатывается для какой-либо конкретной обработки ресурса. По этой причине, ID действий в основном +являются глаголами, такими как `view`, `update`, и т. д. + +По-умолчанию, ID действия должен содержать только следующие символы: Английские буквы в нижнем регистре, цифры, +подчеркивания и дефисы. Дефисы в ID действий используются для разделения слов. Например, `view`, `update2`, `comment-post` являются +допустимыми ID действий, в то время как `view?`, `Update` не являются таковыми. + +Вы можете создавать действия двумя способами: встроенные действия и отдельные действия. Встроенное действие является методом, определенным +в классе контроллера, в то время как отдельное действие является экземпляром класса, унаследованного от [[yii\base\Action]] или его потомков. +Встроенные действия требуют меньше усилий для создания и в основном используются если у вас нет надобности в повторном использовании действий. +Отдельные действия, с другой стороны, в основном создаются для использования в различных контроллерах или при использовании в [расширениях](structure-extensions.md). + + +### Встроенные действия + +Встроенные действия это те действия, которые определены в рамках методов контроллера, как мы это уже обсудили. + +Названия методов действий могут быть получены из 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()]] в вашем классе контроллера, следующим образом: + +```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 + + +Возвращаемое значение методов действий или метода `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'); +} +``` + + +### Параметры действий + +Методы действий для встроенных действий и методы `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). + + +### Действие по-умолчанию + +Каждый контроллер имеет действие, указанное через свойство [[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'); + } +} +``` + + +## Жизненный цикл контроллера + +При обработке запроса, [приложение](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). + + +## Лучшие практики + +В хорошо-организованных приложения, контроллеры обычно очень тонкие, и содержат лишь несколько строк кода. +Если ваш контроллер слишком сложный, это обычно означает, что вам надо провести рефакториг его и перенести какой-либо код +в другие места. + +В целом, контроллеры + +* могут иметь доступ к данным [запроса]runtime-requests.md); +* могут вызывать методы [моделей](structure-models.md) и других компонентов системы с данными запроса; +* могут использовать [представления](structure-views.md) для формирования ответа; +* не должны заниматься обработкой данных, это должно происходить в [моделях](structure-models.md); +* должны избегать использования HTML или другой разметки, лучше это делать в [представлениях](structure-views.md).