Конструктор списков

Целью создания и использования конструктора списков является минимизация дублирования кода, скорость разработки и простота создания списков и форм сущностей модулей или дополнений.

Список состоит из следующих элементов:

  • табы
  • фильтры списка
  • столбцы списка
  • список записей в табличном виде
  • постраничная навигация (pagination)
  • столбец действий с элементами списка: редактирование, удаление записи и другие

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

CREATE TABLE `bff_posts` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL DEFAULT '', -- название
  `enabled` tinyint(1) unsigned NOT NULL DEFAULT '1', -- включена ли публикация
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, -- дата создания
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Для формирования списка можно выделить несколько логических шагов:

  1. Cоздаём объект списка $list и передаем ему объект контроллера $this
$list = \tplAdmin::blockList($this);
  1. Задаем столбцы списка $list->column(ID столбца, Название, Доп. параметры).
$list->column('id', 'ID')
     ->column('title', 'Название')
     ->column('created', 'Дата создания');
  1. Добавляем обработчик события переключения состояния записи onToggle().
    Необходимо это для возможности указать публикуется ли запись непосредственно из списка.
$list->onToggle(function($id, $type){
   # требует необходимого метода в модели,
   # выполняющего изменение поля `enabled` для записи по eё $id
   $this->model->postToggle($id, $type);
});

Состояние определяет поле enabled, обязательно должно присутствовать в данных записи, полученных из модели при отрисовке списка (далее в пункте 6).

В обработчик события переключения состояния записи $list->onToggle(callback) передаем callback, параметрами которого являются:

  • id идентификатор записи
  • type название поля, по которому происходит переключение состояния записи (в нашем случае и по умолчанию 'enabled'): опубликована 1 / снята с публикации 0
  1. Добавляем обработчики события удаления записей, для возможности удалить запись непосредственно из списка.
$list->onDelete(function($id) {
    # удаляем запись путем вызова соответствующего метода модели 
    $res = $this->model->postDelete($id);
    if ( ! $res) {
        $this->errors->impossible();
        return;
    }
});

Аналогично в обработчик события удаления записи передаем callback, отвечающий за удаление записи. Вторым параметром может передаваться callback, выполняющий логику перед удалением записи.

  1. Задаем параметры постраничной навигации списка. Метод total(count) позволяет использовать пагинацию по умолчанию (с заданным количеством записей на страницу 20шт). Для этого достаточно сообщить списку общее количество записей, имеющихся в базе данных.
$count = $this->model->postsListingCount(); // получаем этого количество от модели
$list->total($count);

Установить необходимое вам количество записей на странице пагинации можно следующим образом:

$list->perPage(10);
  1. Получаем записи и добавляем их в список.
    Данные о каждой записи в нашем примере должны содержать поля: id, title, created, enabled
    В случае если в базе данных поле хранящее состояние публикации имеет другое название, например publicated, тогда при формировании выборки используем alias 'publicated as enabled'.
$rows = $this->model->postsListing($filter); // получаем данные от модели
$list->rows($rows);
  1. Для вывода списка используем метод view, результатом котого будет итоговая HTML страница списка.
    Метод контроллера должен вернуть этот результат приложению.
return $list->view();

В качестве примера использования все логические шаги объединены в метод админ-контроллера отвечающий за формирование списка:

/** 
 * Пример метода админ-контроллера модуля/дополнения
 * формирующего список публикаций
 * @return string HTML
 */
public function listing()
{
    # создаем объект списка
    $list = \tplAdmin::blockList($this);

    # задаем табы для списка
    # для возможности фильтрации по статусу публикации
    $list->tabs()
         ->add(0, 'Все')
         ->add(1, 'Опубликованные')
         ->add(2, 'Неопубликованные');

    # задаем столбцы списка
    $list
        ->column('id', 'ID', 80)
        ->column('title', 'Название', false, [
            'align' => $list::COLUMN_ALIGN_LEFT, # выравниваем по левому краю
        ])
        ->column('created', 'Дата создания', 130)
    ;

    # добавляем действие переключения состояния записи
    $list->onToggle(function($id, $type){
        $this->model->postToggle($id, $type);
    });

    # добавляем действие удаления записи
    $list->onDelete(function($id) {
        if ( ! $id) {
            $this->errors->impossible();
            return;
        }
        # получаем данные о записи по $id
        $data = $this->model->postData($id);
        if (empty($data)) {
            $this->errors->impossible();
            return;
        }
        # удаляем запись
        $res = $this->model->postDelete($id);
        if ( ! $res) {
            $this->errors->impossible();
            return;
        }
    });

    # формируем фильтр выборки и порядок сортировки по табу
    $orderBy = 'id';
    $filter = [];
    switch ($list->tab()) {
        case 0: # Все
            # Сортировка по умолчанию
            break;
        case 1: # Опубликованные
            $filter['enabled'] = 1;
            break;
        case 2: # Неопубликованные
            $filter['enabled'] = 0;
            break;
    }

    # постраничная навигация (по умолчанию 20 записей на странице)
    $list->perPage(10); # устанавливаем кол-во записей на странице в значение 10
    $list->perPageRange([10 => '10', -1 => 'Все']); # фомируем доступные варианты выбора кол-ва
    $list->total($this->model->postsListingCount($filter)); # запрашиваем общее кол-во у модели

    # получаем данные о записях
    $rows = $this->model->postsListing($filter, $orderBy);
    $list->rows($rows);
    
    # отрисовываем список и возвращаем его приложению
    return $list->view();
}

Формирование списка в отдельном шаблоне

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

$list = \tplAdmin::blockList($objectController, 'listing');

В котором указывает название php шаблона tpl/listing.php контроллера модуля/дополнения где и выполняется инициализация всех элементов списка. Внутри шаблона объект списка будет доступен в виде локальной переменной $list, а объект контроллера как $this.

<?php
/**
 * @var $list \bff\tpl\admin\BlockList
 */

$list->column('id', 'ID', 80)
     ->column('title', 'Название');

// ...

Список и формы

Для обработки событий добавления и редактирования записи следует использовать компонент Формы. Подробнее о порядке применения форм в списках читайте в данной статье.

Ротация

За включение ротации (перемещение записей выше/ниже путем перетягивания) отвечает обработчик события ротации onRotate().

Обычно сортировку записей выполняют в определенном табе списка. Для осуществления такой сортировки необходимо:

  1. наличие поля в базе данных, отвечающего за положение записи в общем списке, обычно в базе данных это поле числового типа:
`num` int(11) unsigned DEFAULT '0',  
  1. при задании тaбов укажите дополнительный атрибут ротации в настройках таба вот таким образом:
# задаем табы для списка
$list->tabs()
     ->add(0, 'Все')
     ->add(1, 'Избранные', true, ['rotate' => true]); # таб с включенной ротацией
  1. Добавьте обработчик события ротации $list->onRotate(callback), который принимает callback отвечающий за ротацию записей:
$list->onRotate(function($tab_id){
    $this->model->db->rotateTablednd(`bff_posts`, '', 'id', 'num');
});
  1. Выполнить сортировку записей можно предусмотренным для этого стандартным методом rotateTablednd(), параметрами которого являются название таблицы - bff_posts, дополнительные параметры SQL запроса - '', название поля id записи - id, название поля, по которому осуществляется сортировка - num.
  2. Задайте параметры сортировки при выводе полей необходимого таба:
switch ($list->tab()) {
    case 0: # Все
        # Сортировка по умолчанию
        break;
    case 1: # Избранные
        $orderBy = 'num'; # сортировка по полю num
        break;
}
  1. Если необходимо выполнять ротацию в пределах определенной выборки, нужно указать дополнительные параметры запроса для функции rotateTablednd() в виде SQL условия:
$this->model->db->rotateTablednd(`bff_posts`, 'AND status = 1', 'id', 'num');

В таком случае важно также учитывать, что поле num в таблице должно присваиваться при создании новой записи с учетом данного условия выборки.

Избранные записи

Метод onToggle() может принимать в качестве второго параметра значение 'fav'. В этом случае переключатель будет иметь вид добавления в избранное.
Поле в базе данных также должно называться 'fav'.

$list->onToggle(function($id, $type){
   # вызываем метод модели для изменения значения поля `fav` записи по eё $id
   $this->model->postToggle($id, $type);
}, 'fav');

Динамические свойства

В списках доступно подключение компонента динамических свойств.
Подключение выполняется при помощи метода onDynprops(dynpropsObject), параметром является объект компонента динамических свойств. Кнопка управления динамическими свойствами записи появляется в столбце "Действие".