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

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

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

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


dev:controllers:backend:grids

Таблицы в админке

InstantCMS 2 позволяет автоматизировать вывод табличных записей (списков) в админке компонента.

Работу автоматических таблиц проще всего рассматривать на примере.

Пусть это будет иллюстративный компонент «Гостевая книга» (guestbook), который позволяет оставлять сообщения на сайте. У каждого сообщения есть три поля: имя автора (author), дата публикации (date_pub) и текст сообщения (content). В админке компонента мы хотим выводить все сообщения в одной таблице.

Предположим, что у нас уже создан контроллер админки (backend.php) и в нем определен action index:

class backendGuestbook extends cmsBackend { 
    public function actionIndex(){
 
    }
}

Сообщения хранятся в таблице guestbook_messages в базе данных. Таблица имеет поля: id, author, date_pub, content.

Модель компонента имеет метод getMessages(), возвращающий список сообщений и метод getMessagesCount(), возвращающий количество сообщений:

class modelGuestbook extends cmsModel {
    public function getMessages(){
        return $this->get('guestbook_messages');
    }
    public function getMessagesCount(){
        return $this->getCount('guestbook_messages');
    }    
}

Теперь мы хотим чтобы в админке компонента экшен index получал из базы все сообщения и выводил их в виде таблицы.

Создание описания таблицы

Для генерации таблицы системе необходимо знать какие колонки она будет содержать. Для этого создается файл описания. Такие файлы лежат в папке /system/controllers/{компонент}/backend/grids. Каждый файл имеет название grid_{название_таблицы}.php. Внутри файла определяется функция grid_{название_таблицы}(), возвращающая массив с тремя полями:

optionsОпции таблицы
columnsОписание столбцов (полей) таблицы
actionsДействия с каждой строкой (редактировать, удалить и т.п.)

В нашем примере таблица может называться messages и располагаться в файле /system/controllers/guestbook/backend/grids/grid_messages.php. Внутри объявлена функция:

function grid_messages($controller){
    $options = array();
    $columns = array();
    $actions = array();
 
    return array(
        'options' => $options,
        'columns' => $columns,
        'actions' => $actions
    );
}

Входящий параметр $controller содержит ссылку на объект контроллера (backendGuestbook).

Опции таблицы

Массив $options возвращаемый выше должен содержать список опций таблицы. Доступные опции:

НазваниеОписаниеВозможные значения
is_sortableТаблицу можно сортировать по столбцамtrue или false
is_filterТаблицу можно фильтровать по некоторым столбцамtrue или false
is_paginationТаблица выводится постраничноtrue или false
is_draggableСтроки таблицы можно перетаскивать мышкой вверх и внизtrue или false
order_byПоле, по которому сортируются строки таблицы по-умолчаниюназвание поля
order_toПорядок сортировки по-умолчаниюasc или desc
show_idПоказывать колонку с полем id в таблицеtrue или false

В нашем примере мы хотим выводить сортируемую таблицу с фильтрами по полям и постраничной разбивкой. Сообщения по-умолчанию сортируются по дате, по убыванию. Заполним массив $options:

    $options = array(
        'is_sortable' => true,   // сортировать по столбцам
        'is_filter' => true,     // фильтровать по некоторым столбцам
        'is_pagination' => true, // выводить постранично
        'is_draggable' => false, // нельзя перетаскивать мышкой
        'order_by' => 'date_pub',// сортировать по дате публикации
        'order_to' => 'desc',    // по убыванию (сначала новые)
        'show_id' => true        // показывать столбец id
    );

Поля таблицы

Массив $columns в описании таблицы будет содержать описание для каждого столбца (поля). Каждый элемент в $columns соответствует одному полю, а ключ элемента – названию поля во входящих данных (которые мы будем брать из модели).

Например, добавим столбец с полем id:

    $columns = array(
        'id' => array(
            'title' => 'id', // Заголовок столбца
            'width' => 30, // Ширина столбца, в пикселях
            'filter' => 'exact' // Фильтр по столбцу - искать точные совпадения
        )
    );

Каждое поле может иметь несколько опций:

НазваниеОписание
titleЗаголовок столбца в таблице
widthШирина столбца, в пикселях или процентах (в этом случае указывается как строка, например «40%»)
hrefСсылка поля. Позволяет делать поля ссылками. Например, если нужно чтобы значение поля ссылалось на форму редактирования соответствующей записи. Подробнее ниже
href_handler :!: Для InstantCMS 2.7.0 и выше. Анонимная функция function($row){}. Получает всю запись (строку) в виде массива. Должна вернуть true или false. Если возвращает true – поле будет ссылкой (если опция href задана). Если возвращает false, то даже при заданной опции href поле не будет ссылкой.
flagЕсли указано true, то позволяет выводить значения поля в виде галочки. Если поле содержит единицу или не пустое – будет выведена зеленая галочка, если 0 или пустое – серая галочка
flag_onПозволяет указать точное значение, при котором поле считается заполненным. В этом случае, зеленая галочка будет выводиться только при совпадении значения поля с указанным
flag_toggleПозволяет указать URL, на который будет посылаться уведомление о кликах по галочке в поле. Можно использовать, например, для быстрого переключения значения поля одним кликом. Обработчик по указанному URL должен вернуть JSON-объект с двумя полями: error – Наличие ошибки (true или false), is_on – Новое состояние флага (true или false). Можно использовать универсальный URL
filterВключает фильтр для столбца. В качестве значения передается тип фильтра – exact (точное совпадение) или like (частичное совпадение). Если указан тип фильтра, столбец будет содержать текстовое поле для ввода условия фильтра
handlerАнонимная функция с двумя входящими параметрами: function($field, $row){ }, где $field – значение текущего поля, $row – вся строка целиком. Функция должна вернуть обработанное значение $field. Применяется для форматирования выводимых в таблице значений (например, чтобы добавить валюту к ценам в числовых полях)
editable Включает инлайн редактирование поля. Опция содержит массив, который может содержать две ячейки: table (обязательная) и save_action (необязательная). В ячейке table указывается название таблицы, в которую изменения этого поля будут вноситься, в ячейке save_action указывается экшен, который будет принимать данные для сохранения. Если ячейка save_action не указана, то используется системный экшен, в большинстве случаев его будет достаточно. Обратите внимание, в таблице table должно быть инкрементное поле id с уникальным идентификатором записи.

Для нашего примера с гостевой книгой массив столбцов будет выглядеть так:

$columns = array(
    'id' => array(
        'title' => 'id',
        'width' => 30,
        'filter' => 'exact'
    ),
    'author' => array(
        'title' => LANG_AUTHOR,
        'filter' => 'like'
    ),
    'date_pub' => array(
        'title' => LANG_DATE,
        'filter' => 'like', 
        'handler' => function ($field, $row){
            return date('d.m.Y', strtotime($field));
        }
    ),
    'content' => array(
        'title' => LANG_MESSAGE,
        'filter' => 'like', 
        'handler' => function ($field, $row){
            return mb_strlen($field) > 100 ? mb_substr($field, 0, 100) : $field;
        }
    )
);

Поля date_pub и content мы дополнительно обрабатываем с помощью опции handler. В первом случае мы приводим дату в нужный формат, во-втором – обрезаем сообщения длиннее 100 символов. Для поля id включаем строгий фильтр, для остальных полей – фильтры по частичному совпадению.

Ссылки из полей таблицы

При выводе полей в таблице любое из них можно сделать активной ссылкой. Для этого используется опция href в описании поля (столбца). Значением этой опции может быть URL ссылки. Внутри URL можно подставлять значения любых полей текущей записи (строки) таблицы. Это делается с помощью выражения {имя_поля}.

Например, каждое сообщение в гостевой книге доступно по адресу http://site/guestbook/message/123, где 123 - ID сообщения в базе. Мы хотим чтобы текст каждого сообщения в нашей таблице сообщений в админке был ссылкой на страницу сообщения. Тогда мы можем добавить опцию href для столбца content:

    'content' => array(
        'title' => LANG_MESSAGE,
        'filter' => 'like', 
        'handler' => function ($field, $row){
            return mb_strlen($field) > 100 ? mb_substr($field, 0, 100) : $field;
        },
        'href' => href_to('guestbook', 'message', '{id}')
    )

Для формирования URL мы используем функцию href_to(), которая возвращает URL требуемого экшена с параметрами. В качестве параметра мы указываем {id}, то есть значение соответствующего поля для данной строки.

Действия таблицы

В последнем столбце таблицы обычно располагаются иконки действий, которые можно совершить с каждой строкой (такие как редактирование, удаление и т.п.):

Массив $actions в описании таблицы содержит список доступных действий для строк. Каждое действие представлено вложенным массивом, который может иметь следующие поля:

titleЗаголовок действия, отображается при наведении мыши на иконку
classCSS-класс, в котором описана иконка действия. Подробнее см. ниже
hrefСсылка (URL) на экшен, выполняющий данное действие
confirmТекст подтверждения, который будет показан при попытке совершить действие. Например, «Вы уверены?». В тексте можно использовать подстановку полей, с помощью выражения {имя_поля}
handlerАнонимная функция function($row){}. Получает всю запись (строку) в виде массива. Должна вернуть true или false. Если возвращает false – данное действие не будет отображаться для данной строки. Используется, например, для запрета удаления определенных строк

В нашем примере с гостевой книгой каждое сообщение может иметь 3 действия – «посмотреть на сайте», «редактировать», «удалить»:

$actions = array(
    array(
        'title' => LANG_VIEW,
        'class' => 'view',
        'href' => href_to('guestbook', 'message', '{id}')
    ),
    array(
        'title' => LANG_EDIT,
        'class' => 'edit',
        'href' => href_to($controller->root_url, 'edit', '{id}')
    ),
    array(
        'title' => LANG_DELETE,
        'class' => 'delete',
        'href' => href_to($controller->root_url, 'delete', '{id}'),
        'confirm' => LANG_GUESTBOOK_MESSAGE_DELETE_CONFIRM,
    ),
);

Первое действие ссылается на экшен фронтенда – /guestbook/message, а следующие два – на экшены админки. В этом случае вместо названия контроллера в начало URL подставляется свойство $controller→root_url, содержащее корневой путь для backend-контроллера компонента в админке.

Для действия «Удалить» используется подтверждение, заданное константой в языковом файле компонента:

define('LANG_GUESTBOOK_MESSAGE_DELETE_CONFIRM', 'Удалить сообщение №{id}?');

В сообщении выражение {id} будет заменено на ID удаляемой строки.

Иконки действий

Для вывода иконки действия используется CSS-класс. Стандартные классы:

Класс → Иконка Класс → Иконка
view accept
edit play
config rss
labels user
fields unbind
filter delete
permissions relations

Вы можете определить собственный класс my_class в файле /templates/default/controllers/{компонент}/backend/styles.css:

.datagrid .actions a.my_class { background-image: url("../../images/icons/my_icon.png"); }

В этом примере будет использоваться иконка /templates/default/images/icons/my_icon.png.

Вывод таблицы

После того, как готов файл с описанием структуры таблицы, необходимо реализовать ее отображение и заполнение данными. Этот процесс состоит из нескольких этапов:

  1. Тело таблицы выводится в шаблоне нужного экшена;
  2. Отправляется AJAX-запрос для получения данных;
  3. Полученные данные добавляются в таблицу;
  4. При изменении страницы, сортировки или фильтров таблицы - возврат к п.2.

Вывод таблицы в шаблоне

Чтобы вывести таблицу в шаблоне сначала необходимо загрузить ее описание. Это делается в коде экшена при помощи собственного метода loadDataGrid($grid_name):

$grid = $this->loadDataGrid('my_grid');

Для сохранения сортировки в вашей таблице (если сортировка предполагается), используйте следующий код для загрузки описания таблицы при её выводе:

$grid = $this->loadDataGrid('my_grid', false, 'my_component.my_key');

где my_component - имя вашего компонента (например guestbook), my_key - некий ключ, однозначно идентифицирующий данную таблицу.

Затем полученный массив $grid передается в шаблон, где выводится с помощью метода renderGrid($data_url, $grid):

<?php $this->renderGrid($this->href_to('action'), $grid); ?>

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

В результате на странице формируется тело таблицы, т.е. пустая таблица, имеющая только заголовки столбцов. В таком виде страница отправляется пользователю. Данные подгружаются уже после вывода страницы пользователю, асинхронно.

Вернемся к нашему примеру с гостевой книгой. Добавим загрузку и вывод таблицы в контроллере админке и шаблоне.

Контроллер /system/controllers/guestbook/backend.php:

class backendGuestbook extends cmsBackend { 
    public function actionIndex(){
 
        // загружаем описание таблицы    
        $grid = $this->loadDataGrid('messages');
 
        // подключаем шаблон и передаем описание в него
        return cmsTemplate::getInstance()->render('backend/index', array(
            'grid' => $grid
        )); 
 
    }
}

Шаблон /templates/default/controllers/guestbook/backend/index.tpl.php:

<h3>Список сообщений</h3>
<?php $this->renderGrid($this->href_to('messages_ajax'), $grid); ?>

Загрузка данных в таблицу

Для загрузки данных обычно используется отдельный экшен, который ничего не выводит в браузер, а только возвращает JSON-массив со строками таблицы.

Внутри экшена происходит следующее:

  1. Загрузка описания таблицы;
  2. Подключение модели компонента;
  3. Получение данных о фильтрах из запроса;
  4. Если есть сохраненный ранее фильтр - получение его, либо сохранение полученного выше;
  5. Применение фильтров к модели;
  6. Получение отобранных данных из модели;
  7. Формирование и вывод результата в виде JSON.

В примере выше мы указали в шаблоне экшен messages_ajax как источник данных при построении таблицы.

Добавим этот экшен в контроллер:

public function actionMessagesAjax(){
 
    // проверяем что это AJAX-запрос
    if (!$this->request->isAjax()) { cmsCore::error404(); }
 
    // загружаем описание таблицы
    $grid = $this->loadDataGrid('messages');
 
    // получаем собственную модель
    $model = cmsCore::getModel($this->name);
 
    // устанавливаем кол-во строк на одной странице таблицы по-умолчанию
    $model->setPerPage(admin::perpage);
 
    // подготавливаем массив, в котором будут содержаться
    // условия фильтрации (в т.ч. номер страницы и сортировка)
    $filter = array();
 
    // получаем описание фильтров из запроса
    $filter_str = $this->request->get('filter', '');
 
    // если в $filter_str ничего нет, то загрузим ранее сохранённый фильтр, если он есть, 
    // либо, если в $filter_str есть данные - обновим фильтр
    $filter_str = cmsUser::getUPSActual('my_component.my_key', $filter_str);
 
    if ($filter_str){ // если есть фильтры,
        // парсим полученную строку с условиями и заполняем массив $filter
        parse_str($filter_str, $filter);
 
        // применяем фильтры таблицы к модели
        $model->applyGridFilter($grid, $filter);
    }
    // получаем общее кол-во строк, с учетом всех фильтров
    $total = $model->getMessagesCount();
 
    // получаем – сколько строк на странице, либо значение по-умолчанию
    $perpage = isset($filter['perpage']) ? $filter['perpage'] : admin::perpage;
 
    // вычисляем количество страниц
    $pages = ceil($total / $perpage);
 
    // получаем из модели записи (строки), удовлетворяющие условиям фильтрации
    $messages = $model->getMessages();
 
    // подключаем объект шаблона
    $template = cmsTemplate::getInstance();
 
    // просим шаблон отрендерить JSON-массив со строками таблицы
    $template->renderGridRowsJSON($grid, $messages, $total, $pages);
 
    $this->halt(); // завершаем работу
}

Обратите внимание, что делать разбор фильтров и применять их к модели имеет смысл только если ваша таблица имеет постраничную разбивку, сортировку или собственно фильтры (т.е. включена любая из опций – is_sortable, is_filter или is_pagination).


Здесь был рассмотрен простейший случай, когда вся информация хранилась в одной таблице БД.

Разумеется, в реальной практике может понадобиться выводить данные из нескольких связанных таблиц. Что и нужно сделать обычным путём, описанном, прежде всего, в главе «Модель». Более того, вы можете сделать таблицу, которая выводит данные не из Базы Данных, или из БД с другого сайта и т.п. Важно лишь, чтобы в строке:

$template->renderGridRowsJSON($grid, $data, $total, $pages);

структура таблицы в параметре $grid соответствовала структуре массива $data. Тогда всё будет корректно.


Вернуться к оглавлению раздела "Контроллеры"

dev/controllers/backend/grids.txt · Последнее изменение: 30.01.2017 23:39 — fuze