Как повесить выполнение метода на форму action html формы?
Роутинг — это маршрутизация: входящий URL разбирается специальным образом и по его результату выполняется определенный код (или как в вопросе метод класса). С роутингом напрямую связано понятие ЧПУ (человекопонятные урлы), которое позволяет исключить в адресах сложные параметры. Например вместо http://сайт/admin/new-page пришлось бы использовать http://сайт/admin.php?action=new-page
Примеры готовых систем маршрутизации:
- https://github.com/bramus/router
- https://github.com/nikic/FastRoute
- https://github.com/izniburak/php-router
- https://github.com/mrjgreen/phroute
- https://github.com/delight-im/PHP-Router
Отслеживать
ответ дан 17 дек 2019 в 11:52
1,935 1 1 золотой знак 8 8 серебряных знаков 20 20 бронзовых знаков
- php
- html
-
Важное на Мете
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.15.1019
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Отдельные действия контроллеров Yii2
Отдельные действия контроллеров в фреймворке Yii2 не создаются в теле самого контроллера и не имеют стандартного названия типа actionIndex(). Они указываются в карте действий контроллера, в методе actions() . Сам же функционал такие действия хранят в отдельных классах.
- Функциональные. Главное их назначение — обработать какие-то данные, а дальше могут на этом остановиться (при использовании PJAX) или осуществить перенаправление, могут вывести страницу .
- Вывод статической страницы. Назначение — вывод запрашиваемой страницы на экран без необходимости создания отдельного действия в контроллере, которое выводит страницу методом render().
ФУНКЦИОНАЛЬНЫЕ
Начну сразу с примера. Создаем класс действия в common\components\HelloWorldAction.php
Для создания отдельного действия, вы должны наследоваться от класса [[yii\base\Action]] или его потомков, и реализовать метод run() с областью видимости public.
Теперь подключим действие, например в frontend\controllers\SiteController.php
public function actions() < return [ 'hi' =>[ 'class' => 'app\components\HelloWorldAction', ], ]; >
Название нашего действия — ‘hi’. Чтобы вызвать его, нужно набрать в адресной строке:
http://site.com/site/hi
В данном примере возвращаемое методом run() значение будет выведено пользователю в виде строки. На практике же, в run() могут вызываться другие методы, можно получать параметры из форм, например:
Yii::$app->request->get()
делать переадресацию:
return Yii::$app->response->redirect('http://klisl.com/service.html');
То есть таким образом можно вынести функционал из методов контроллера в отдельный класс. Похожее значение имеют подключаемые пользовательские классы, например с помощью поведений. Но они не являются действиями, и не получится передать в них напрямую, например, параметры при отправке пользователем формы.
Вывод статической страницы.
Удобно использовать когда не нужно формировать и передавать данные в файл вид. При этом не понадобится создавать несколько однотипный действий контроллера такого плана:
public function actionContact() < return $this->render('signup'); >
Для этого используется встроенный класс \yii\web\ViewAction, который предназначен для вывода статических страниц.
Подключим его в frontend\controllers\SiteController.php
public function actions() < return [ 'page' =>[ 'class' => 'yii\web\ViewAction', ], ]; >
Файл представление в данном случае размещается в: frontend\views\site\pages\contact.php
где pages – название папки по-умолчанию. Название можно поменять если добавить в массив следующим элементом после указания класса:
'viewPrefix' => 'tables' //название папки c видами
папку можно и удалить
'viewPrefix' => ''
тогда файл вид будет с другими файлами в frontend\views\site\contact.php
Ссылка для вывода страницы на экран будет такого плана:
http://site.com/site/page?view=contact
Согласно ссылки – нужно поместить файл вид contact.php в папку frontend\views\site\pages . В названии файла не должно быть нижнего подчеркивания.
Данный способ работает только для передачи названия страницы в качестве GET параметра. Но можно сделать, чтобы ссылка выглядела привычным образом, в данном случае так:
http://site.com/page/contact
Для этого в frontend\config\main.php
'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ //Преобразовывает в GET типа site/page?view=. 'page/' => 'site/page', … ] ],
данное правило преобразовывает данные в GET параметры для использования в классе ViewAction.
Автор: Сергей Дата публикации: 18.02.2017
- Создание REST API для проекта на Yii2.
- Yii2 — подтверждение регистрации на сайте по email.
- Создание расширения (пакета) для Yii2.
- Мультиязычный сайт на Yii2.
- ЧПУ ссылки (URL) для сайта на Yii2 с использованием слов вместо идентификаторов.
Комментарии (без регистрации)
Макс 29.04.2017 18:00
Спасибо, стало чуть понятнее.
Правильно ли я понял:
Отдельные действия хорошо подходят, если:
1. Нужно выводить статические страницы, чтобы не писать одинаковые методы в контроллере, мы просто добавив в actionView в карту действий контроллера, а страницы будем вызывать через get, просто указывая названия файла, типа mysite.com/info зайдет в папку где лежит view файл info.php
2. Более сложнее, если нужен отдельный функционал, например какой-то компонент, в карте действий указываем ссылку на данный класс(компонент) и при get вызове данного действия(например название auth), это уже будет обрабатывать. Если это было в SiteController, то при вызове mysite.com/auth задача SiteController’a просто перенаправить на тот класс что указан в карте контроллера. Это избавляет нас от создания лишнего файла в контроллере, а так же от добавления лишнего кода и тд.
ответ на комментарий Макс от 29.04.2017

Сергей (администратор) 29.04.2017 23:22
Да, для статических страниц, где не нужно обрабатывать данные и передавать их в вид, отдельные действия вполне подходят. Название страницы, а точнее файла-вида, который нужно вывести, передается с помощью GET запроса. А вот URL, по-умолчанию, должен быть такого плана:
http://mysite.com/site/page?view=info
где info – имя файла-представления.
Чтобы было формата mysite.com/info, нужно будет создать соответствующие правила для URL-менеджера как я писал в конце статьи.
Касательно 2-го пункта — ключ массива, содержащего класс
'hi' => [
'class' => 'app\components\HelloWorldAction',
],
тут это «hi», представляет из себя то же самое что и название обычного действия контроллера. И вызывается так же — в зависимости от get-параметра. Только вместо выполнения кода действия контроллера выполняется метод run() класса, указанного в значении массива.
Использовать стоит в случае, когда нужно отделить функционал от контроллера. URL, по которому будет вызываться нужный класс, так же настраивается в urlManager наряду с обычными действиями контроллера.
Михаил 15.05.2018 22:00
Здравствуйте, спасибо за статью!
Нашел опечатку в коде, блок 4:
public function actions() < return [ 'page' => [ 'class' => 'yii\web\ViewAction', ], ]; >
для Вашего примера будет правильно именовать экшн не page, а contact, иначе утверждение
Файл представление в данном случае размещается в: frontend\views\site\pages\contact.php
не сработает.
ответ на комментарий Михаил от 15.05.2018

Сергей (администратор) 28.05.2018 11:14
Yii контроллеры, экшены, фильтры и немного о производительности

Я не ошибусь если предположу, что большинство современных фреймворков для разработки web-приложений основаны на паттерне MVC (модель-представление-контроллер). Не буду углубляться в суть этого подхода, интересующимся можно прочитать об этом тут. Yii не исключение. Для обработки запроса пользователя, необходимо создать контроллер, который может содержать несколько, так называемых «экшенов». Рассмотрим пример. Пусть, нам необходимо разработать функционал для работы с постами (допустим мы разрабатываем блог). Под «работой» я понимаю, то, что нам необходимо обеспечить возможность выполнения следующих операций — создание поста, изменение и удаление. Как правило конкретную работу выполняют как раз экшены, а контроллер служит своего рода объединяющим контейнером для них. В Yii контроллером является класс производный от CController, таким образом простейший контроллер может быть иметь следующий вид:
class PostController extends CController
Yii накладывает несколько ограничений на объявление класса контроллера:
1. Наименование класса должно быть следующим Controller — окончание «Controller» — является обязательным. Пример: PostController.
2. Название файла, содержащего контроллер должно совпадать с наименованием класса. Пример: PostController.php.
Как говорилось выше, основные действия выполняют именно экшены, рассмотрим их реализацию в Yii.
Экшеном (action) может быть как метод контроллера, так и отдельный класс.
В простейшем и самом распространенном варианте, экшеном является метод контроллера. Здесь Yii так же накладывает свои правила:
1. Название метода должно содержать префикс «action».
// создаем action — create public function actionCreate()
И так мы имеем простейший контроллер, который имеет следующий вид:
class PostController extends CController < public function actionCreate() < echo 'Вызван экшн create!'; >>
Сохраним данный код в файле PostController.php, и поместим его в каталог Controllers приложения.
Теперь мы можем обратиться к нашему экшену, используя следующий url:
(тут я предполагаю что Yii-приложение расположено в каталоге testDrive, в корне web-сервера)
Параметр r, который имеет значение post/create, в терминах Yii, называется роутом. Как несложно догадаться, первая часть роута (post) — обозначает идентификатор контроллера (PostController), вторая часть (create) — идентификатор экшена (в данном случае мы вызываем экшн actionCreate, в контроллере PostController).
Кроме стандартного способа создания экшенов (как методов контроллера), Yii позволяет создавать экшены в виде отдельных классов (В Codeigniter и Kohana такого делать нельзя, Symfony, кажется, умеет так).
Экшеном является класс, унаследованный от CAction.
// экшн выполненный в виде отдельного класса class ActionShow extends CAction < public function run() < //логика экшена располагается здесь echo "Экшн в виде отдельного класса!" . >>
Сохраним данный код в файле ActionShow.php и поместим его в каталог
/testDrive/protected/controllers/post/ (его необходимо создать).
Так как мы «вынесли» экшн в отдельный класс, необходимо как-то сообщить нашему контроллеру где искать класс экшена. Для этого необходимо переопределить метод actions контроллера, который должен вернуть массив, следующего вида:
array( 'show' => 'application.controllers.post.ActionShow' );
‘show’ — имя вызываемого экшена;
‘application.controllers.post.ActionShow’ — алиас пути, иными словами путь к файлу экшена в структуре приложения Yii (физически это путь к файлу /testDrive/protected/controllers/post/ActionShow.php)
Определив метод actions, наш контроллер будет иметь следующий вид:
class PostController extends CController < public function actions() < return array( 'show' =>'application.controllers.post.ActionShow' ); > public function actionCreate() < echo 'Вызван экшн create!'; >>
Теперь при обращении к url
мы должны увидеть вызов метода run, класса ActionShow.
Возникает вопрос: «для чего выносить экшены в отдельные классы?»
Как отмечается в документации Yii, это позволяет организовать приложение по модульному принципу, таким образом один и тот же экшн, реализованный в виде отдельного класса, может быть использован несколькими контроллерами. Лично мне, тоже, гораздо удобнее работать с php файлом, в котором расположено минимум php-кода. Вынести экшн в отдельный класс это конечно же хорошо и удобно — но есть другая сторона вопроса — производительность. Как мне кажется (внутренности Yii я не смотрел), при рализации экшена как отдельного класса, на плечи Yii ложится несколько дополнительных задач, а именно:
1. Необходимо преобразовать alias пути в реальный путь до класса экшена.
2. Необходимо подключить файл экшена.
3. Необходимо создать экземпляр класса экшена.
4. Необходимо выполнить метод run ().
Мне было интересно какой и способов реализации экшенов работает быстрее и насколько. Для этого я провел небольшое тестирование.
Был написан следующий контроллер:
/** * Description of TestActionController * * @author andrey */ class TestActionController extends CController < //put your code here public function actions() < return array( 'outer' =>'application.controllers.outer.OuterAction' ); > public function actionInline() < print "inline action. "; >>
Внешний экшн имеет вот такой вид:
/** * Description of OuterAction * * @author andrey */ class OuterAction extends CAction < //put your code here public function run() < print "outer action. "; >>
Для тестирования, утилита ab (apache bench) обращалась к следущим url:
localhost/yii/index.php/TestAction/outer — для тестирования экшена, выполненного в виде отдельного класса
localhost/yii/index.php/TestAction/inline — для тестирование экшена, выполнненого в виде метода контроллера
Тестирование проводилось на моей домашней машине: core2duo 2.2, 2GB — ОП, Ubuntu 8.10, Apache 2.2.9, php 5.2.6.
Команда для тестирования ab -t 10 -c 10 url.
И вот такие получились результаты.
экшн как метод контроллера ~ 92-95 rps
экшн как отдельный класс ~ 87-90 rps
Как видно из результатов, производительность отличается, примерно на 2-5 запросов в секунду, в пользу «встроенных» экшенов. Конечно, для очень больших и нагруженных проектов — даже эти 5 запросов очень важны, однако для меня же удобнее использовать «внешние» экшены, несмотря на «мизирный» проигрыш в скорости работы.
Хотелось бы сказать еще несколько слов о фильтрах. Согласно документации фильтр — это код, который может выполняться до и/или после выполнения любого экшена. Как и сами экшены — фильтры могут быть реализованы двумя способами:
* как метод контролера
* как отдельный класс
При реализации фильтра как метода контроллера, необходимо создать метод с префиксом «filter», пример:
// фильтр как метод контроллера public function filterInAction($filterChain) < // код фильтра . $filterChain->run(); >
1. Префикс «filter» — обязателен.
2. Модификатор доступа для метода фильтра — public.
3. Строка $filterChain->run () — обязательна для выполнения цепочки фильтров и запрошенного экшена.
4. Встроенные фильтры могут выполняться только перед выполнением экшена (я не нашел способа выполнить встроенный фильтр после экшена)
Для того что-бы контроллер «знал» о необходимости выполнения фильтра — необходимо переопределить его метод «filters», пример:
public function filters() < return array( 'InAction' // указываем название метода фильтра БЕЗ префикса "filter" ); >
Данный код говорит о том, что фильтр «InAction» — будет выполнен при обращеннии к любому экшену контроллера. Для того что бы исключить некоторые экшены, необходимо переписать метод вот так:
public function filters() < return array( 'InAction — inline' // указываем название метода фильтра БЕЗ префикса "filter" ); >
фрагмент строки «- inline» — говорит о том, что экшн «inline» не будет обработан фильтром.
Второй способ реализации фильтров — в виде отдельного класса. Для этого необходимо создать класс, производный от CFilter и переопределить его методы.
class OuterFilter extends CFilter < public function preFilter($filterChain) < // код выполняемый перед экшеном . return true; >public function postFilter($filterChain) < // код выполняемый после экшена . >>
1. Оба метода должны принимать один параметр — $filterChain
2. Для того что бы продолжилось выполнение экшена или следующего в цепочке фильтра, метод preFilter должен вернуть true (если вернуть false — выполнение прекращается. )
Для использования внешнего фильтра, так как же и при использовании «встроенного» — необходимо переопределить метод filters контроллера.
public function filters()
Обратите внимание на то, что при указании «внешнего» фильтра, элементом массива фильтров, является так же массив, а не строка, как при объявлении «внутренних» фильтров. Используя такой подход можно передавать дополнительные параметры в фильтр, например вот так:
public function filters() < return array( 'InAction — inline', array( 'application.filters.OuterFilter', 'clean' =>'all' ) ); >
В этом примере, мы передаем параметр «clean» со значением «all».
Так же как и при работе со «встроенными» фильтрами, можно указывать какие
экшены контроллера стоит обрабатывать, а какие нет.
// пример — исключаем экшн "inline" из обработки фильтром "OuterFilter" public function filters() < return array( 'InAction — inline', array( 'application.filters.OuterFilter — inline', 'clean' =>'all' ) ); >
При использовании внешних фильтров, так же как и при использовании внешних экшенов, Yii должен проделать некоторую дополнительную работу.
1. Необходимо преобразовать alias пути в реальный путь до класса фильтра.
2. Необходимо подключить файл фильтра.
3. Необходимо создать экземпляр класса фильтра.
4. Необходимо выполнить методы preFilter () и postFiltrer ().
На выполнение этих действий, затрачиваются дополнительные ресурсы и время. Мне было интересно провести испытания, аналогичные с испытаниями для экшенов. Получились следующие результаты.
Встроенный экшн + встроенный фильтр ~ 89 — 90 rps;
Встроенный экшн + внешний фильтр ~ 87 — 89 rps;
Внешний экшн + встроенный фильр ~ 84 — 88 rps;
Внешний экшн + внешний фильр ~ 87 — 90 rps;
Применение внешних фильтров дает следующие преимущества:
1. Код становится более модульным, упрощается повторное использование
2. Внешние фильтры, в отличии от встроенных, позволяют выполнять действия как до, так и после выполнения экшена.
Лично для себя я решил следующее:
Несмотря на то, что реализация экшенов и фильтров как отдельных классов, несколько снижает производтельность приложения, я все таки выбираю этот подход — мне так значительно проще и комфортнее ориентироваться в структуре проекта, несмотря на то, что количество файлов увеличивается.
Каждый выбирает свой способ реализации, надеюсь, эта статья поможет Вам сделать выбор!
Контроллеры ¶
Контроллеры являются частью 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 https://hostname/index.php?r=site/index , то index действие в site контроллере будет вызвано. Секция Маршрутизация содержит более подробную информацию о том как маршруты сопоставляются с действиями.
Создание контроллеров ¶
В Веб приложениях, контроллеры должны быть унаследованы от yii\web\Controller или его потомков. Аналогично для консольных приложений, контроллеры должны быть унаследованы от 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 в пространстве имен контроллеров. Допустимыми символами для префиксов подпапок являются: Английские буквы в нижнем и верхнем регистре, символы подчеркивания и слэш, где слэш используется в качестве разграничителя для многовложенных подпапок (например panels/admin ).
Правила наименования классов контроллеров ¶
Названия классов контроллеров могут быть получены из ID контроллеров следующими способами:
- Привести в верхний регистр первый символ в каждом слове, разделенном дефисами. Обратите внимание что, если ID контроллера содержит слэш, то данное правило распространяется только на часть после последнего слэша в ID контроллера;
- Убрать дефисы и заменить любой прямой слэш на обратный;
- Добавить суффикс Controller ;
- Добавить в начало пространство имен контроллеров.
Ниже приведены несколько примеров, с учетом того, что пространство имен контроллеров имеет значение по умолчанию равное 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/post-comment должен находиться в файле @app/controllers/admin/PostCommentController.php .
Информация: Последний пример admin/post-comment показывает каким образом вы можете расположить контроллер в подпапке пространства имен контроллеров. Это очень удобно, когда вы хотите организовать свои контроллеры в несколько категорий и не хотите использовать модули.
Карта контроллеров ¶
Вы можете сконфигурировать карту контроллеров для того, чтобы преодолеть описанные выше ограничения именования ID контроллеров и названий классов. В основном это очень удобно, когда вы используете сторонние контроллеры, именование которых вы не можете контролировать.
Вы можете сконфигурировать карту контроллеров в настройках приложения следующим образом:
[ 'controllerMap' => [ // объявляет "account" контроллер, используя название класса 'account' => 'app\controllers\UserController', // объявляет "article" контроллер, используя массив конфигурации 'article' => [ 'class' => 'app\controllers\PostController', 'enableCsrfValidation' => false, ], ], ]
Контроллер по умолчанию ¶
Каждое приложение имеет контроллер по умолчанию, указанный через свойство yii\base\Application::$defaultRoute. Когда в запросе не указан маршрут, тогда будет использован маршрут указанный в данном свойстве. Для Веб приложений, это значение ‘site’ , в то время как для консольных приложений, это ‘help’ . Таким образом, если задан URL https://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 или его потомков, и реализовать метод run() с областью видимости public. Роль метода run() аналогична другим методам действий. Например,
namespace app\components; use yii\base\Action; class HelloWorldAction extends Action < public function run() < return "Hello World"; > >
Результаты действий ¶
Возвращаемое значение методов действий или метода run() отдельного действия очень важно. Оно является результатом выполнения соответствующего действия.
Возвращаемое значение может быть объектом response, который будет отослан конечному пользователю в качестве ответа.
- Для Веб приложений, возвращаемое значение также может быть произвольными данными, которые будут присвоены yii\web\Response::$data, а затем конвертированы в строку, представляющую тело ответа.
- Для Консольных приложений, возвращаемое значение также может быть числом, представляющим статус выхода исполнения команды.
В вышеприведенных примерах, все результаты действий являются строками, которые будут использованы в качестве тела ответа, высланного пользователю. Следующий пример, показывает как действие может перенаправить браузер пользователя на новый URL, с помощью возврата response объекта (т. к. redirect() метод возвращает response объект):
public function actionForward() < // перенаправляем браузер пользователя на https://example.com return $this->redirect('https://example.com'); >
Параметры действий ¶
Методы действий для встроенных действий и методы run() для отдельных действий могут принимать параметры, называемые параметры действий. Их значения берутся из запросов. Для Веб приложений, значение каждого из параметров действия берется из $_GET , используя название параметра в качестве ключа; для консольных приложений, они соответствуют аргументам командной строки.
В приведенном ниже примере, действие view (встроенное действие) определяет два параметра: $id и $version .
namespace app\controllers; use yii\web\Controller; class PostController extends Controller < public function actionView($id, $version = null) < // . > >
Для разных запросов параметры действий будут определены следующим образом:
- https://hostname/index.php?r=post/view&id=123 : параметр $id будет присвоено значение ‘123’ , в то время как $version будет иметь значение null , т. к. строка запроса не содержит параметра version ;
- https://hostname/index.php?r=post/view&id=123&version=2 : параметрам $id и $version будут присвоены значения ‘123’ и ‘2’ соответственно;
- https://hostname/index.php?r=post/view : будет брошено исключение yii\web\BadRequestHttpException, т. к. обязательный параметр $id не был указан в запросе;
- https://hostname/index.php?r=post/view&id[]=123 : будет брошено исключение yii\web\BadRequestHttpException, т. к. параметр $id получил неверное значение [‘123’] .
Если вы хотите, чтобы параметр действия принимал массив значений, вы должны использовать type-hint значение array , как показано ниже:
public function actionView(array $id, $version = null) < // . >
Теперь, если запрос будет содержать URL https://hostname/index.php?r=post/view&id[]=123 , то параметр $id примет значение [‘123’] . Если запрос будет содержать URL https://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 действия не указан, то будет использовано ID действия по умолчанию;
- Если ID действия найдено в карте действий, то отдельное действие будет создано;
- Если ID действия соответствует методу действия, то встроенное действие будет создано;
- В противном случае, будет выброшено исключение yii\base\InvalidRouteException.
- Контроллер последовательно вызывает метод beforeAction() приложения, модуля (если контроллер принадлежит модулю) и самого контроллера.
- Если один из методов вернул false , то остальные, не вызванные методы beforeAction будут пропущены, а выполнение действия будет отменено;
- По-умолчанию, каждый вызов метода beforeAction() вызовет событие beforeAction , на которое вы можете назначить обработчики.
- Контроллер запускает действие:
- Параметры действия будут проанализированы и заполнены из данных запроса.
- Контроллер последовательно вызывает методы afterAction контроллера, модуля (если контроллер принадлежит модулю) и приложения.
- По-умолчанию, каждый вызов метода afterAction() вызовет событие afterAction , на которое вы можете назначить обработчики.
- Приложение, получив результат выполнения действия, присвоит его объекту response.
Лучшие практики ¶
В хорошо организованных приложениях контроллеры обычно очень тонкие и содержат лишь несколько строк кода. Если ваш контроллер слишком сложный, то обычно это означает, что вам надо провести его рефакторинг и перенести часть кода в другие места.
В целом, контроллеры
- могут иметь доступ к данным запроса;
- могут вызывать методы моделей и других компонентов системы с данными запроса;
- могут использовать представления для формирования ответа;
- не должны заниматься обработкой данных, это должно происходить в слое моделей;
- должны избегать использования HTML или другой разметки, лучше это делать в представлениях.
