Документация InstantCMS

для администраторов и разработчиков

Инструменты пользователя

Инструменты сайта


dev:controllers:actions

Действия контроллеров

Контроллер представляет из себя класс, наследуемый от системного класса 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()). В качестве аргумента он принимает название экшена, определенное системным роутером. Далее этот метод может совершать любые нужные проверки. В итоге он должен вернуть название экшена который будет вызван.


Вернуться к оглавлению

dev/controllers/actions.txt · Последнее изменение: 17.10.2017 00:16 — fuze