bp/app/Views/components/table/README.md

8.2 KiB
Raw Blame History

DataTable Components

Переиспользуемые компоненты для отображения интерактивных таблиц с AJAX-загрузкой, сортировкой и поиском.

Структура компонентов

public/
├── js/
│   └── modules/
│       └── DataTable.js      # JS-модуль для инициализации таблиц
└── css/
    └── components/
        └── data-table.css    # Стили для интерактивных таблиц

app/Views/components/table/
├── table.twig           # Основной компонент таблицы
├── table_header.twig    # Переиспользуемый заголовок
└── pagination.twig      # Компонент пагинации

Быстрый старт

1. Подключение стилей и скриптов

В вашем шаблоне добавьте:

{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="/css/components/data-table.css">
{% endblock %}

{% block scripts %}
{{ parent() }}
<script src="/js/modules/DataTable.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
    new DataTable('your-table-id', {
        url: '/your-module/table',
        perPage: 10
    });
});
</script>
{% endblock %}

2. Использование компонента таблицы

{{ include('@components/table/table.twig', {
    id: 'products-table',
    url: '/products/table',
    perPage: 25,
    sort: sort|default(''),
    order: order|default('asc'),
    filters: filters|default({}),
    columns: [
        { name: 'name', label: 'Название', width: '35%' },
        { name: 'sku', label: 'Артикул', width: '15%' },
        { name: 'price', label: 'Цена', width: '15%' },
        { name: 'stock', label: 'Остаток', width: '15%' }
    ],
    actions: { label: 'Действия', width: '20%' },
    emptyMessage: 'Товары не найдены'
}) }}

Конфигурация столбцов

Каждый столбец поддерживает следующие параметры:

Параметр Тип Описание
name string Идентификатор столбца (используется для сортировки и фильтрации)
label string Отображаемое название столбца
width string Ширина столбца (например, '35%', '200px')
placeholder string Текст-подсказка в поле поиска
searchTitle string Title для иконки поиска
align string CSS-класс выравнивания

Конфигурация пагинации

Компонент автоматически получает данные из объекта pager:

// В контроллере
$pagination = [
    'currentPage' => $pager->getCurrentPage(),
    'totalPages' => $pager->getPageCount(),
    'totalRecords' => $pager->getTotal(),
    'perPage' => $perPage,
    'from' => (($pager->getCurrentPage() - 1) * $perPage + 1),
    'to' => min($pager->getCurrentPage() * $perPage, $pager->getTotal())
];

Пример контроллера

public function table()
{
    $page = (int) ($this->request->getGet('page') ?? 1);
    $perPage = (int) ($this->request->getGet('perPage') ?? 10);
    $sort = $this->request->getGet('sort') ?? '';
    $order = $this->request->getGet('order') ?? 'asc';
    
    // Фильтры
    $filters = [
        'name' => $this->request->getGet('filters[name]') ?? '',
    ];
    
    // Построение запроса
    $builder = $this->model->builder();
    
    // Применяем фильтры
    if (!empty($filters['name'])) {
        $builder->like('name', $filters['name']);
    }
    
    // Сортировка
    if (!empty($sort)) {
        $builder->orderBy($sort, $order);
    }
    
    // Пагинация
    $items = $builder->paginate($perPage, 'default', $page);
    
    $data = [
        'items' => $items,
        'pager' => $this->model->pager,
        'perPage' => $perPage,
        'sort' => $sort,
        'order' => $order,
        'filters' => $filters,
    ];
    
    return $this->renderTwig('path/to/your/_table', $data);
}

AJAX-ответ

Для AJAX-запросов контроллер должен возвращать только tbody и tfoot:

{# _table.twig для модуля #}
{% set isAjax = app.request.headers.get('X-Requested-With') == 'XMLHttpRequest' %}

{% if isAjax %}
    {# AJAX: только tbody #}
    <tbody>
        {% for item in items %}
        <tr>
            <td>{{ item.name }}</td>
            <td>{{ item.price }}</td>
            <td class="text-end">
                <a href="...">Редактировать</a>
            </td>
        </tr>
        {% endfor %}
    </tbody>
    
    {% if items is not empty and pager %}
    <tfoot>
        <tr>
            <td colspan="3">
                {{ include('@components/table/pagination.twig', { 
                    pagination: paginationData,
                    id: 'your-table-id'
                }) }}
            </td>
        </tr>
    </tfoot>
    {% endif %}
{% else %}
    {# Обычный запрос: полная таблица #}
    <div class="table-responsive">
        {{ include('@components/table/table.twig', {
            id: 'your-table-id',
            url: '/your-module/table',
            perPage: perPage,
            columns: columns,
            pagination: paginationData,
            actions: { label: 'Действия' },
            emptyMessage: 'Нет данных'
        }) }}
    </div>
{% endif %}

API DataTable

Опции при инициализации

new DataTable('container-id', {
    url: '/api/endpoint',      // URL для AJAX-загрузки
    perPage: 10,               // Записей на странице по умолчанию
    debounceTime: 300,         // Задержка поиска в мс
    preserveSearchOnSort: true // Сохранять видимость поиска при сортировке
});

Методы

const table = new DataTable('my-table', options);

// Установка фильтра
table.setFilter('columnName', 'value');

// Установка количества записей
table.setPerPage(25);

// Переход на страницу
table.goToPage(3);

Доступные CSS-классы

Класс Описание
.data-table Основной контейнер таблицы
.header-content Контейнер для элементов заголовка
.header-text Текст заголовка столбца
.search-trigger Иконка поиска
.sort-icon Иконка сортировки
.header-search-input Поле ввода поиска
.sort-icon.active Активная сортировка
.pagination-wrapper Обёртка пагинации

Расширение функциональности

Добавление кастомных действий

Для добавления кнопок действий в строки:

{% for client in clients %}
<tr>
    <td>{{ client.name }}</td>
    <td>{{ client.email }}</td>
    <td class="actions-cell text-end">
        <a href="{{ editUrl }}" class="btn btn-sm btn-outline-primary">
            <i class="fa-solid fa-pen"></i>
        </a>
    </td>
</tr>
{% endfor %}

Кастомные строки

Компонент поддерживает произвольное содержимое ячеек через параметр rows:

{% set rows = [] %}
{% for product in products %}
{% set rows = rows|merge([{
    cells: [
        { content: '<strong>' ~ product.name ~ '</strong>', class: '' },
        { content: product.price ~ ' ₽', class: 'text-end' }
    ],
    actions: '<a href="...">Редактировать</a>'
}]) %}
{% endfor %}

{{ include('@components/table/table.twig', {
    id: 'products-table',
    rows: rows,
    columns: columns,
    ...
}) }}