Содержание
Действия контроллеров
Контроллер представляет из себя класс, наследуемый от системного класса cmsFrontend или cmsBackend для публичной и административной части сайта соответственно.
Здесь и далее мы будем рассматривать все на примере frontend-контроллера. Особенности реализации backend-контроллера рассмотрены в отдельной статье.
Класс контроллера
Класс определяется в файле /system/controllers/{имя компонента}/frontend.php.
В простейшем случае класс выглядит так:
class shop extends cmsFrontend { }
где shop - название компонента, совпадающие с названием его папки.
Все публичные методы, объявленные в этом классе доступны через контекст $this во внешних экшенах и хуках.
В методах контроллера вы можете подключать и объекты других контроллеров. Есть два способа.
Первый способ:
// получаем объект контроллера Контент $content = cmsCore::getController('content'); // получаем субъекты правил $perms = $content->getPermissionsSubjects();
Второй способ (InstantCMS выше 2.8.2 версии):
// получаем субъекты правил $perms = $this->controller_content->getPermissionsSubjects();
Важно отметить, что эти два действия отличаются контекстом создания объекта контроллера. У метода getController есть второй параметр - $request
, где можно передать объект запроса. Во втором способе объект запроса передаётся тот же, что и у текущего контроллера, в данном случае объекта класса shop. Т.е. если переписать первый способ вот так:
// получаем объект контроллера Контент $content = cmsCore::getController('content', $this->request); // получаем субъекты правил $perms = $content->getPermissionsSubjects();
то примеры по сути будут идентичны с разницей лишь в удобстве использования.
Определение действий
Действие контроллера (экшен) - это дочерний метод класса контроллера, обрабатывающий запросы пользователя через URL.
Например, пользователь открывает страницу site.ru/shop/catalog/view/123. Роутер InstantCMS произведет разбор данного адреса на составные части:
- shop - компонент, frontend-контроллер которого нужно запустить
- catalog - экшен контроллера, который нужно вызвать
- все что далее, в нашем случае view и 123 - параметры, которые нужно передать экшену
Таким образом, чтобы обработать подобный URL мы должны создать файл /system/controllers/shop/frontend.php со следующим содержимым:
class shop extends cmsFrontend { public function actionCatalog($do, $id){ dump("Hello, do = {$do}, id = {$id}"); } }
В результате, после открытия URL site.ru/shop/catalog/view/123 пользователь увидит в браузере текст:
Hello, do = view, id = 123
Разумеется, в реальности экшен не должен совершать вывод на экран с помощью dump() или echo. Вместо этого используются шаблоны, работа с которыми будет рассмотрена в следующих разделах.
Индексное действие
В случаях, когда в URL содержится только название компонента, без указания экшена будет вызван метод actionIndex().
Названия действий
Название метода каждого экшена в контроллере формируется по принципу: action{Name}, где {Name} - полученный из URL идентификатор экшена.
В URL название экшена может состоять из нескольких слов, разделенных знаком подчеркивания (_). В этом случае название метода формируется по принципу: action{ManyWordsName}. Каждая заглавная буква в {ManyWordsName} соответствует знаку подчеркивания в URL.
Примеры преобразования имен:
URL | Контроллер | Метод контроллера |
---|---|---|
site.ru/shop | shop | actionIndex() |
site.ru/shop/catalog | shop | actionCatalog() |
site.ru/shop/view_order | shop | actionViewOrder() |
site.ru/shop/add_many_items | shop | actionAddManyItems() |
Действия во внешних файлах
Экшены могут быть определены как внутри класса контроллера (в файле frontend.php), так и в собственных отдельных файлах. В случаях, когда контроллер содержит большое количество объемных экшенов целесообразнее вынести их в отдельные файлы.
Файлы хранятся в папке /system/controllers/{имя компонента}/actions.
Название файла должно совпадать с названием экшена в URL страницы.
Например, рассмотренный выше экшен actionCatalog() для компонента shop может быть вынесен в файл /system/controllers/shop/actions/catalog.php.
Внутри файла должен быть определен класс action{Имя компонента}{Название экшена}, наследуемый от системного класса cmsAction:
class actionShopCatalog extends cmsAction { }
Внутри класса должен быть определен метод run():
class actionShopCatalog extends cmsAction { public function run($do, $id){ dump("Hello from file! Do = {$do}, id = {$id}"); } }
Метод run() принимает параметры экшена из URL, аналогично с тем, как это делал сам экшен когда был определен в frontend.php.
Важным моментом в данном случае является то, что внутри метода run() доступны все методы и свойства самого контроллера (класса, определенного в frontend.php) через $this.
Например, frontend.php:
class example extends cmsFrontend { public $my_name = 'John'; public function sayHello($your_name) { dump("Hello, {$your_name}!", false); } }
actions/hello.php:
class actionExampleHello extends cmsAction { public function run(){ $this->sayHello('Vasiliy'); dump("My name is {$this->my_name}"); } }
В этом случае обращение по URL site.ru/example/hello выведет:
Hello, Vasiliy! My name is John
Таким образом, код который должен быть доступен внутри любого экшена (не зависимо от его расположения) можно размещать внутри класса контроллера.
Иногда, по каким-то причинам разработки, необходимо написать экшен, который будет вызываться из других контроллеров или иным способом, но не напрямую через URL. Для того, чтобы защититься от прямых вызовов таких экшенов существует свойство $lock_explicit_call
. Установите его в true
.
class actionUsersProfile extends cmsAction { public $lock_explicit_call = true; public function run(){ // PHP code } }
Действия before и after
Иногда может потребоваться выполнить одинаковый код в начале или конце каждого экшена (например, провести какую-либо инициализацию или очистку). В этом случае нет необходимости дублировать код в каждом экшене. Вместо этого можно объявить специальные методы before() и after() в теле контроллера.
Пример:
class shop extends cmsFrontend { public function before($action_name){ // Эта строка обязательна parent::before($action_name); // Код внутри этого метода будет выполнен // ПЕРЕД запуском любого экшена контроллера dump("Before: {$action_name}", false); return true; } public function after($action_name){ // Эта строка обязательна parent::after($action_name); // Код внутри этого метода будет выполнен // ПОСЛЕ запуска любого экшена контроллера dump("After: {$action_name}"); } public function actionCatalog($do, $id){ dump("do = {$do}, id = {$id}", false); } }
Методы before() и after() имеют входящий параметр - название экшена до/после которого они вызываются. Это позволяет разграничить логику для каждого конкретного экшена.
Таким образом, для нашего примера, если пользователь откроет URL site.ru/shop/catalog/view/123 он получит вывод:
Before: catalog do = view, id = 123 After: catalog
Метод before() должен возвращать true или false. Если он вернет false - вызываемый экшен не будет выполнен, но никаких ошибок при этом также не будет показано. Если внутри before() необходимо выдать 404 ошибку, то необходимо использовать метод ядра:
cmsCore::error404();
Изменение роутинга
Иногда может потребоваться изменить системную логику роутинга экшенов.
Например, мы захотим чтобы адреса профилей пользователей на сайте имели вид: site.ru/users/{id}, где {id} - идентификатор пользователя в базе. В этом случае мы получим множество разных адресов:
- site.ru/users/1
- site.ru/users/24
- site.ru/users/6
- и так далее
Стандартный роутер при обращении, например, к адресу site.ru/users/24 будет пытаться вызвать метод action24() в контроллере компонента users. Нам же в данном случае нужно, чтобы второй сегмент в URL отвечал не за название экшена, а за параметр какого-либо из них.
Мы можем определить собственный метод для роутинга, внутри контроллера:
class users extends cmsFrontend { public function routeAction($action_name){ // Если название вызываемого экшена состоит НЕ из одних цифр, // то возвращаем исходное название, как ни в чем не бывало if (!is_numeric($action_name)){ return $action_name; } // иначе, понимаем что в $action_name на самом деле id пользователя $user_id = $action_name; // сохраняем полученный ID как первый параметр для того экшена, // на который будет перенаправлять работу array_unshift($this->current_params, $action_name); // возвращаем название нужного экшена return 'view_profile'; } public function actionViewProfile($id) { if (!is_numeric($id)) { cmsCore::error404(); } dump("Это профиль пользователя ID = {$id}"); } }
Метод routeAction() запускается до вызова указанного в URL экшена (но после before()). В качестве аргумента он принимает название экшена, определенное системным роутером. Далее этот метод может совершать любые нужные проверки. В итоге он должен вернуть название экшена который будет вызван.