Контроллеры являются частью MVC архитектуры. Это объекты классов, унаследованных от cot\controllers\BaseController, отвечающие за обработку запроса и генерирование ответа. В сущности, после обработки запроса приложением, контроллеры проанализируют входные данные, извлекают нужные данные из БД, передают результаты в шаблонизатор, и в конечном итоге генерируют исходящие ответы.
Контроллеры - это альтернатива хукам module и standalone.
Контроллеры состоят из действий, которые являются основными блоками, к которым может обращаться конечный пользователь и запрашивать исполнение того или иного функционала. В контроллере может быть одно или несколько действий.
Действия - это методы класса контроллера, начинающиеся с префикса action
Следующий пример показывает post контроллер с двумя действиями: view и create:
namespace cot\modules\post\controllers;
use cot\controllers\BaseController;
use cot\exceptions\NotFoundHttpException;
class PostController extends BaseController
{
public function actionView(): string
{
$postId = cot_import('id', 'G', 'INT');
$post = PostRepository::getById($id);
if ($post === null) {
throw new NotFoundHttpException;
}
$template = cot_tplfile('blog.post');
$t = new XTemplate($template);
$t->assign([
'ID' => $post['id'],
'TITLE' => htmlspecialchars($post['title']),
'TEXT' => $post['text'],
]);
$t->parse('MAIN');
return $t->text('MAIN');
}
public function actionCreate()
{
$title = cot_import('title', 'P', 'TXT');
$text = cot_import('text', 'P', 'TXT');
Cot::$db->insert(Cot::$db->posts, ['title' => $title, 'text' => $text]);
$id = Cot::$db->lastInsertId();
cot_redirect(cot_url('post', ['n' => 'post', 'a' => 'view', 'id' => $id], '', true));
}
}В действии view (определенном методом actionView()), код сначала загружает данные согласно запрошенному ID поста. Если данные успешно загружены, то код отобразит ее с помощью шаблонизатора. В противном случае будет брошено исключение.
В действии create (определенном методом actionCreate()), код получает данные из запроса и сохраняет их. Потом перенаправляет браузер на действие view с ID только что созданной записи.
Конечные пользователи обращаются к действиям через так называемые маршруты. Маршрут это строка, состоящая из следующих частей:
GET параметр e;GET параметр n. Cтрока, которая уникально идентифицирует контроллер среди всех других контроллеров одного и того же расширения;GET параметр a. строка, которая уникально идентифицирует действие среди всех других действия одного и того же контроллера.Маршруты могут иметь следующий формат:
https://your-domain.tld?e=extensionCode&n=ControllerID&a=ActionID
Таким образом, если пользователь запрашивает URL https://your-domain.tld?e=blog&n=posts&a=view&id=123 то view действие контроллера posts расширения blog будет вызвано.
Контроллеры должны быть унаследованы от cot\controllers\BaseController или его потомков
Следующий код определяет post контроллер:
namespace cot\modules\blog\controllers;
use cot\controllers\BaseController;
class PostController extends BaseController
{
}Контроллеры располагаются в папке controllers в корневой папке расширения. Контроллеры административной части сайта располагаются в папке controllers/admin.
Обычно контроллер сделан таким образом, что он должен обрабатывать запросы, связанные с определенным ресурсом. Именно по этим причинам, ID контроллеров обычно являются существительные, ссылающиеся на ресурс, который они обрабатывают. Например, вы можете использовать article в качестве ID контроллера, которые отвечает за обработку данных статей.
По-умолчанию, ID контроллеров должны содержать только следующие символы: Английские буквы в нижнем регистре, цифры, подчеркивания, тире и слэш. Например, оба article и post-comment являются допустимыми ID контроллеров, в то время как article?, PostComment, admin\post не являются таковыми.
Названия классов контроллеров могут быть получены из ID контроллеров следующими способами:
Controller;Ниже приведены несколько примеров, с учетом того, что пространство имен контроллеров имеет значение по умолчанию равное cot\modules\blog\controllers:
article соответствует cot\modules\blog\controllers\ArticleController;post-comment соответствует cot\modules\blog\controllers\PostCommentController;Классы контроллеров должны быть автозагружаемыми. Именно по этой причине, в вышеприведенном примере, контроллер article должен быть сохранен в файл modules/blog/controllers/ArticleController.php
Если в маршруте отсутствует ID контроллера, будет вызван IndexController, если он есть.
Создание действий не представляет сложностей так же как и объявление так называемых методов действий в классе контроллера. Метод действия это public метод, имя которого начинается с префикса action. Возвращаемое значение метода действия представляет собой ответные данные, которые будут высланы конечному пользователю. Приведенный ниже код определяет два действия index и hello-world:
namespace cot\modules\blog\controllers;
use cot\controllers\BaseController;
class PostController extends BaseController
{
public function actionIndex(): string
{
$template = cot_tplfile('blog.index');
$t = new XTemplate($template);
$t->parse('MAIN');
return $t->text('MAIN');
}
public function actionHelloWorld(): string
{
return 'Hello World';
}
}В основном действие разрабатывается для какой-либо конкретной обработки ресурса. По этой причине, ID действий в основном являются глаголами, такими как view, update, и т.д.
По-умолчанию, ID действия должен содержать только следующие символы: Английские буквы в нижнем регистре, цифры, подчеркивания и дефисы. Дефисы в ID действий используются для разделения слов. Например, view, update2, comment-post являются допустимыми ID действий, в то время как view?, Update не являются таковыми.
Вы можете создавать действия двумя способами: встроенные действия и отдельные действия. Встроенное действие является методом, определенным в классе контроллера, в то время как отдельное действие является экземпляром класса, унаследованного от cot\controllers\BaseAction или его потомков. Встроенные действия требуют меньше усилий для создания и в основном используются если у вас нет надобности в повторном использовании действий. Отдельные действия, с другой стороны, в основном создаются для использования в различных контроллерах.
Встроенные действия это те действия, которые определены в рамках методов контроллера, как мы это уже обсудили.
Названия методов действий могут быть получены из ID действий следующим образом:
Например, index соответствует actionIndex, а hello-world соответствует actionHelloWorld
Названия имен действий являются регистрозависимыми. Если у вас есть метод ActionIndex, он не будет учтен как метод действия, таким образом, запрос к действию index приведет к выбросу исключения. Также следует учесть, что методы действий должны иметь область видимости public. Методы имеющие область видимости private или protected НЕ определяют методы встроенных действий.
Встроенные действия в основном используются, потому что для их создания не нужного много усилий. Тем не менее, если вы планируете повторно использовать некоторые действия в различных местах, или если вы хотите перераспределить действия, вы должны определить его как отдельное действие.
Отдельные действия определяются в качестве классов, унаследованных от cot\controllers\BaseAction или его потомков.
Для использования отдельного действия, вы должны указать его в карте действий, с помощью переопределения метода cot\controllers\BaseController::actions() в вашем классе контроллера, следующим образом:
public static function actions(): array
{
return [
// объявляет "error" действие с помощью названия класса
'error' => cot\modules\blog\controllers\actions\ErrorAction::class,
// объявляет "view" действие с помощью конфигурационного массива
'view' => [
'class' => cot\modules\blog\controllers\actions\ViewAction::class,
'viewPrefix' => '',
],
];
}Как вы можете видеть, метод actions() должен вернуть массив, ключами которого являются ID действий, а значениями - соответствующие названия класса действия или конфигурация. В отличие от встроенных действий, ID отдельных действий могут содержать произвольные символы, до тех пор пока они определены в методе actions().
Для создания отдельного действия, вы должны наследоваться от класса cot\controllers\BaseAction или его потомков, и реализовать метод run() с областью видимости public. Роль метода run() аналогична другим методам действий. Например:
namespace cot\modules\blog\controllers\actions;
use cot\controllers\BaseAction;
class HelloWorldAction extends BaseAction
{
public function run(): string
{
return "Hello World";
}
}Возвращаемое значение методов действий или метода run() отдельного действия очень важно. Оно является результатом выполнения соответствующего действия.
Возвращаемое значение может быть строкой, которая будет использована в качестве тела ответа, высланного пользователю.
Следующий пример, показывает как действие может перенаправить браузер пользователя на новый URL:
public function actionForward()
{
// перенаправляем браузер пользователя на https://example.com
cot_redirect('https://example.com');
}Каждый контроллер имеет действие, указанное через свойство cot\controllers\BaseController::$defaultAction. Когда маршрут содержит только ID контроллера, то подразумевается, что действие контроллера по умолчанию было запрошено.
По-умолчанию, это действие имеет значение index. Если вы хотите изменить это значение, просто переопределите данное свойство в классе контроллера следующим образом:
namespace cot\modules\blog\controllers;
use cot\controllers\BaseController;
class PostController extends BaseController
{
public static $defaultAction = 'home';
public function actionHome()
{
return "Hello World";
}
}При обработке запроса, приложение создаст контроллер, основываясь на запрошенном маршруте. Для выполнения запроса, контроллер пройдет через следующие этапы жизненного цикла:
beforeAction(). Если метод вернул false, действие будет отменено.afterAction() в который передается результат выполнения действия. Этот метод может использоваться для постобработки результата.