Версия: 6.x
burger close
Контроллеры

Общие сведения

Контроллерами в ReadyScript называются объекты наследников класса RS::Controller::AbstractController. При запуске контроллер выполняет действие, что обычно подразумевает создание соответствующих моделей и отображение необходимых представлений. Действия - это методы класса контроллера, название которых начинается на action. Действие, выполняемое по-умолчанию у контроллера называется Index, оно выполняется если не указано иное действие в запросе.

В ReadyScript имеется 2 понятия контроллеров:

  • Фронт контроллеры, это те, что получают управление от маршрута сразу при открытии определенного URL. Фронт контроллеры отрабатывают и формируют так называемое "главное содержимое страницы", которое затем обычно отображается в центральной зоне страницы. Фронт контроллер может решить, продолжать ли рендеринг страницы или вернуть, например ошибку 404, или вернуть поток данных, не оборачивая их HTML-кодом.
  • Блок контроллеры, запускаются в процессе рендеринга определенного шаблона или страницы, в том порядке в котором они расположены в шаблоне или в Конструкторе блоков. Блок контроллеры отрабатывая, формируют какой-то участок HTML кода страницы. HTML код блок контроллеров всегда будет являться лишь частью страницы, т.к. до них другие модули, возможно уже сформировали вывод и они не могут отменить его.

Существуют базовые классы фронт и блок контроллеров для клиентской части(frontend) и администраторской (backend).

Контроллеры клиентской части

Базовые классы контроллеров клиентской части располагаются в пространстве имен RS::Controller.

При запуске контроллера выполняется метод exec, который проверяет права на запуск данного контроллера, и если прав достаточно, то происходит запуск действия контроллера.

Контроллер проверяет права к модулю, которому принадлежит контроллер. Права должны быть больше либо равны значению, указанному в свойстве $access_right объекта контроллера. По умолчанию это значение равно 1, т.е. для запуска фронт-контроллера у группы пользователей, от которой происходит запуск, должны быть минимальные права доступа к соответствующему модулю.

Фронт-контроллеры

Фронт-контроллеры с точки зрения кода - это потомки класса RS::Controller::Front. Клиентские фронт-контроллеры должны находиться в пространстве имен ModuleName\Controller\Front. Минимальный фронт-контроллер выглядит следующим образом:

namespace ModuleName\Controller\Front;
class EasyController extends \RS\Controller\Front
{
function actionIndex()
{
// ...
}
}

Для контроллеров разделов, где требуется авторизация, существует базовый класс RS::Controller::AuthorizedFront. Действие такого контроллера будет выполнено, только если пользователь будет авторизован и будет принадлежать требуемой группе пользователей, в противном случае произойдет перенаправление на страницу авторизации.

namespace ModuleName\Controller\Front;
class ProtectedController extends \RS\Controller\AuthorizedFront
{
protected
$need_group = 'reader'; //Пользователь должен состоять в группе reader, для доступа к данному контроллеру
function actionIndex()
{
// ...
}
}

Блок-контроллеры

Блок контроллеры - это потомки класса RS::Controller::Block. Блок контроллеры можно размещать на странице с помощью Конструктора сайта, расположенного в административной части или с помощью конструкции moduleinsert в Smarty. Система отображает блок контроллеры с описаниями в списках, поэтому у блок контроллеров есть свойства $controller_title и $controller_description, которые необходимо заполнять.

Пример простейшего блок-контроллера:

namespace ModuleName\Controller\Block;
class NewsBlockController extends \RS\Controller\Block
{
protected static
$controller_title = 'Свежие новости',
$controller_description = 'Отображает список недавно добавленных новостей';
function actionIndex()
{
// ...
}
}

Блок контроллеры могут иметь собственные настройки. Настройки должны быть возвращены методом getParamObject() в виде объекта RS::Orm::ControllerParamObject. RS::Orm::ControllerParamObject - это ORM-объект с типом хранилища RS::Orm::Storage::Stub (заглушка). Подробно о порядке объявления свойств ORM объектов можно узнать в разделе ORM объекты. Данный объект будет использован для генерации визуальной формы запроса параметров.

Значения парметров будут доступны внутри контроллера с помощью метода getParam. Параметры по-умолчанию можно задать в свойстве $default_params.

class NewsBlockController extends \RS\Controller\Block
{
protected static
$controller_title = 'Свежие новости',
$controller_description = 'Отображает список недавно добавленных новостей';
protected
$default_params = array(
'category_id' => 0, //Значение параметра по-умолчанию
);
function getParamObject()
{
return new \RS\Orm\ControllerParamObject(
new \RS\Orm\PropertyIterator(array(
'category_id' => new Type\Integer(array(
'description' => 'Категория новостей'
))
))
);
}
function actionIndex()
{
$category_id = $this->getParam('category_id');
// ...
}
}

Рекомендуется, чтобы у блок-контроллера можно было настраивать шаблон, в котором будет отображен результат. Это позволит на одной странице размещать один и тот же блок с разным отображением. Для удобства разработчиков в ReadyScript предумсотрен базовый класс RS::Controller:StandartBlock, в котором задан параметр indexTemplate. Большинство блок-контроллеров модулей, идущих в комплекте с системой, являются потомками RS::Controller:StandartBlock.

class NewsBlockController extends \RS\Controller\StandartBlock
{
protected static
$controller_title = 'Свежие новости',
$controller_description = 'Отображает список недавно добавленных новостей';
protected
$default_params = array(
'indexTemplate' => 'blocks/newsblockcontroller/newslist.tpl', //Значение параметра по-умолчанию
);
function actionIndex()
{
// ...
return $this->result->setTemplate( $this->getParam('category_id') );
}
}

Контроллеры административной панели

Базовые классы контроллеров административной части располагаются в пространстве имен RS::Controller::Admin. Для административной панели также существуют фронт и блок контроллеры. Для административной части есть несколько базовых классов разного назначения:

Блок-контроллеры в административной части также присутствуют. Например в системе есть блок модуля Фотографии, который присутствует в формах разных объектов: Товары, Статьи.

Виджет-контроллеры - используются для отображения виджетов на главной странице административной панели. Система автоматически распознает все виджет-контроллеры в пространствах имен: ИМЯ_МОДУЛЯ\Controller\Admin\Widget, и позволяет их вывести на страницу.

Фронт-контроллеры обрабатывают запросы при открытии разделов административной части из пунктов меню.

CRUD-контроллеры содержат реализацию стандартных CRUD(Create-Read-Update-Delete) действий. Большинство контроллеров административной части являются потомками RS::Controller::Admin::Crud.

Отдельно стоит сказать про CRUD контроллер. Он требует минимальных сведений для инициализации и способен полностью организовать администрирование ORM объектов. Такой контроллер имеет не стандартный ход выполнения действия(action) в отличие от других контроллеров. Как только такой контроллер запускается с действием, например Index, он ищет метод с название helper{ИМЯ_ДЕЙСТВИЯ} , т.е. helperIndex. В методе должен инициализироваться визуальный helper, который далее будет использован для построения элементов страницы. Результат выполнения метода helperIndex сохраняется в свойстве класса и доступен в дальнейшем с помощью метода getHelper. Далее выполняется метод actionIndex, в котором происходит действие(загрузка данных из модели или сохранение элементов) и к результату выдается шаблон, полученный от helper'а.

Данный подход необходим для предоставления возможности другим модулям изменять состав визуальных элементов на странице. После формирования helper'a в системе и перед вызовом метода с действием, вызывается событие controller.exec.СОКРАЩЕННОЕ_ИМЯ_КОНТРОЛЛЕРА.ИМЯ_ДЕЙСТВИЯ, которому в качестве параметра передается helper. Данное событие может быть обработано любым подписчиком. Любой обработчик, например, может добавить свою кнопку в панели инструментов или добавить дополнительную колонку в таблицу, и т.д.

Пример простого CRUD-контроллера:

namespace Catalog\Controller\Admin;
use \RS\Html\Table\Type as TableType,
\RS\Html\Filter,
\RS\Html\Table;
class UnitCtrl extends \RS\Controller\Admin\Crud
{
protected
function __construct($param = array())
{
//Передаем экземпляр класса API, с которым контроллер будет взаимодействовать. Потомок \RS\Module\AbstractModel\EntityList
parent::__construct(new \Catalog\Model\UnitApi());
}
function helperIndex()
{
$helper = parent::helperIndex(); //Дополняем helper, определенный у предка
$helper->setTopTitle(t('Единицы измерения')); //Устанавливаем заголовок раздела
$helper->setTable(new Table\Element(array( //Описываем таблицу с объектами
'Columns' => array(
new TableType\Checkbox('id'),
new TableType\Text('title', 'Полное название', array('href' => $this->router->getAdminPattern('edit', array(':id' => '@id')), 'LinkAttr' => array('class' => 'crud-edit'))),
new TableType\Text('stitle', 'Сокращенное название'),
new TableType\Actions('id', array(
new TableType\Action\Edit($this->router->getAdminPattern('edit', array(':id' => '~field~'))),
),
array('SettingsUrl' => $this->router->getAdminUrl('tableOptions'))
),
)
)));
$helper->setFilter(new Filter\Control( array( //Описываем фильтр
'Container' => new Filter\Container( array(
'Lines' => array(
new Filter\Line( array('items' => array(
new Filter\Type\Text('title','Полное наименование', array('SearchType' => '%like%')),
new Filter\Type\Text('stitle','Короткое обозначение', array('SearchType' => '%like%')),
)
))
),
)),
'ToAllItems' => array('FieldPrefix' => $this->api->defAlias())
)));
return $helper;
}
}

Визуальные помощники

Большинство страниц административной панели имеют схожую задачу - выполнение CRUD операций с объектами. Для решения этой задачи обычно требуется следующий набор страниц - страница со списком объектов(виде таблицы, в виде дерева и таблицы, в виде дерева) и страница с формой объекта. При этом на странице со списком объектов должна присутствовать форма поиска объектов, возможность выбора группы объектов, возможность произвести операцию с группой объектов, возможность выбора состава колонок в таблицах, возможность выбора сортировки элементов в таблице.

Для упрощения работы разработчиков, в ReadyScript есть класс RS::Controller::Admin::Helper::CrudCollection - помощник визуальной части, который является агрегатором настроек для всех визуальных элементов на странице. Помощник используется в CRUD контроллере.

Рассмотрим, как инициализируется helper в контроллере:

protected function helperIndex()
{
return new \RS\Controller\Admin\Helper\CrudCollection(
$this, //объект контроллера
$this->api, //объект API
$this->url, //объект \RS\Http\Request
//Сообщаем состав элементов, которые будут на странице.
array(
'paginator', //Отображать пагинатор
'topToolbar' => $this->buttons(array('add')), //В верхней панели инструментов показать кнопку "Добавить" объект
'bottomToolbar' => $this->buttons(array('delete')), //В нижней части отобразить кнопку удалить объекты
'viewAs' => 'table' //Отобразить страницу со списком объектов в табличном виде
//...далее может быть описан состав таблицы, фильтра, и т.д.
)
);
}

И как используется helper непосредственно в действии:

public function actionIndex()
{
$helper = $this->getHelper();
$this->view->assign('elements', $helper->active()); //Helper заполняет сведения из модели и объект helper'а передается в шаблон
return $this->result->setTemplate( $helper['template'] ); //шаблон сообщает сам helper
}

В момент инициализации, helper принимает параметры согласно правилу отсутствия магических массивов. Визуальные помощники позволяют строить стандартные интерфейсы административной части только при помощи PHP объектов, не углубляясь в HTML. Помощник RS::Controller::Admin::Helper::CrudCollection позволяет конструировать разные типы страниц. Установить тип отображаемой страницы позволяют методы:

viewAsAny - произвольная страница с заголовком и кнопками внизу viewAsForm - страница с формой объекта viewAsTable - страница с таблицей объектов viewAsTableTree - страница с деревом слева и с таблицей справа viewAsTree - страница с деревом

Свойства, доступные в действиях

Объект шаблонизатора RS::View::Engine доступен в контроллере через свойство view.

namespace ModuleName\Controller\Front;
class EasyController extends \RS\Controller\Admin\Crud
{
function actionIndex()
{
//Передаем переменные в шаблон Smarty
$this->view->assign(array(
'key' => 'value',
'key2' => 'value2'
));
//Возвращаем HTML
return $this->view->fetch('blocks/easycontroller/template.tpl');
}
}

В контроллере также доступны следущие свойства:

  • app - объект RS::Application::Application, отвечает за параметры текущей страницы. Отвечает за подключаемые JavaScript файлы, CSS файлы, мета-теги, хлебные крошки, HTTP-заголовки, и т.д
  • url - объект RS::Http::Request, отвечает за входящие в скрипт переменные из суперглобальных массивов
  • router - объект RS::Router::Manager, менеджер маршрутов. Используется, для обращения к объекту текущего маршрута или для генерации URL
  • result - объект RS::Controller::Result::Standard, объект стандартизированного результата контроллера

Результат действия контроллера

Ожидается, что действие контроллера вернет либо строку с HTML данными, которая будет отправлена в браузер, либо объект, имплементирующий интерфейс RS::Controller::Result::IResult или RS::Controller::Result::ITemplateResult.

В случае возвращения объекта, система будет располагать более структурированными данными о результате выполнения действия контроллера. Конечные данные, отправляемые в браузер извлекаются из объекта вызовом метода getOutput().

Интерфейс IResult предполагает, что из результата можно извлечь и установить HTML часть результат с помощью метода getHtml и setHtml. Интерфейс ITemplateResult расширяет IResult и добавляет возможность установить и получить шаблон для рендеринга с помощью методов setTemplate и getTemplate, а также получить переменные, которые должны быть переданы в шаблон с помощью метода getTemplateVars.

Для стандартизации частых ответов контроллера, в ReadyScript был разработан базовый класс RS::Controller::Result::Standard. Он имплементирует интерфейс ITemplateResult.

Идея стандартизации ответов в основном актуальна для Ajax запросов к серверу. Для масштабируемости важно, чтобы сервер в ответ на ajax запрос возвращал не HTML, а JSON данные, в которые можно было бы дописывать необходимые секции в случае потребности в расширении. Для одних и тех же по смыслу ответов контроллера, логично в JSON иметь одинаковые структуры данных и названия секций. Именно эту задачу решает класс стандартного ответа RS::Controller::Result::Standard.

Ответы сервера, которые формирует стандартизировано объект RS::Controller::Result::Standard:

  • требуется авторизация
  • действие прошло успешно/неуспешно
  • вернуть текст для уведомления
  • вернуть текст для уведомления об ошибке
  • вернуть информацию об ошибках в форме
  • вернуть текст успешной операции
  • требуется редирект
  • добавить произвольную секцию в JSON

Логика формирования ответов методом RS::Controller::Result::Standard::getOutput:

Если идет ajax запрос, то:

  • возращается JSON, в котором HTML код результата будет возвращен в секции html.

Если идет обычный запрос:

  • будет возвращен HTML