# Компонент динамических таблиц DataTable
## Общее описание
Компонент DataTable представляет собой универсальную систему для отображения интерактивных таблиц с поддержкой AJAX-загрузки данных, сортировки по столбцам, поиска и пагинации. Система построена на трёх уровнях: серверная часть (контроллер с методом `prepareTableData`), клиентская часть (JavaScript-модуль DataTable) и уровень представления (компоненты Twig).
Архитектура компонента обеспечивает бесшовную работу как при серверном рендеринге (первичная загрузка страницы), так и при AJAX-обновлениях (фильтрация, сортировка, пагинация). При серверном рендеринге таблица отображается сразу с данными, при этом JavaScript автоматически определяет наличие данных и пропускает избыточный AJAX-запрос. При любых действиях пользователя (сортировка, фильтрация, переход по страницам) данные подгружаются через AJAX, а клиентский модуль обновляет только tbody и tfoot, сохраняя заголовок таблицы неизменным.
---
## Структура компонентов
### Файловая структура
Компонент таблицы состоит из нескольких файлов, организованных по функциональному признаку. JavaScript-модуль расположен в `public/assets/js/modules/DataTable.js` и отвечает за все интерактивные взаимодействия на клиенте. Стили находятся в `public/assets/css/modules/data-table.css` и обеспечивают визуальное оформление элементов управления таблицей. Шаблоны Twig размещены в директории `app/Views/components/table/` и включают основной компонент таблицы, заголовок, пагинацию и макросы для рендеринга действий.
Основные файлы компонента:
- `table.twig` — универсальный компонент таблицы, включающий заголовок, тело и футер с пагинацией
- `table_header.twig` — переиспользуемый заголовок с поддержкой сортировки и поиска
- `pagination.twig` — компонент пагинации с навигацией по страницам
- `ajax_table.twig` — упрощённый tbody для AJAX-ответов без заголовка
- `macros.twig` — Twig-макросы для рендеринга кнопок действий
### Интеграция с BaseController
Класс BaseController предоставляет готовую инфраструктуру для работы с таблицами через методы `getTableConfig()`, `prepareTableData()`, `renderTable()` и `table()`. Метод `getTableConfig()` возвращает конфигурацию таблицы, определяющую модель данных, колонки, правила поиска и сортировки, а также действия для каждой строки. Метод `prepareTableData()` выполняет всю логику обработки параметров запроса (пагинация, сортировка, фильтрация), формирует данные и возвращает их в структурированном виде для передачи в шаблон. Метод `renderTable()` принимает конфигурацию и возвращает HTML-код таблицы. Метод `table()` является HTTP-обработчиком для AJAX-запросов, который возвращает только tbody и tfoot без заголовка.
---
## Подключение в контроллере
### Конфигурация таблицы
Каждый контроллер модуля должен определить конфигурацию таблицы через метод `getTableConfig()`. Конфигурация представляет собой ассоциативный массив с обязательными и опциональными параметрами. Обязательными параметрами являются `model` (экземпляр модели для выборки данных) и `columns` (описание колонок таблицы). Опциональные параметры позволяют настроить поведение поиска, сортировки, действий и отображения пустого состояния.
```php
class Clients extends BaseController
{
protected ClientModel $clientModel;
public function __construct()
{
$this->clientModel = new ClientModel();
}
protected function getTableConfig(): array
{
return [
'id' => 'clients-table',
'url' => '/clients/table',
'model' => $this->clientModel,
'columns' => [
'name' => ['label' => 'Имя / Название', 'width' => '40%'],
'email' => ['label' => 'Email', 'width' => '25%'],
'phone' => ['label' => 'Телефон', 'width' => '20%'],
'created_at' => ['label' => 'Создан', 'width' => '15%'],
],
'searchable' => ['name', 'email', 'phone'],
'sortable' => ['name', 'email', 'phone', 'created_at'],
'defaultSort' => 'name',
'order' => 'asc',
'actions' => ['label' => 'Действия', 'width' => '15%'],
'actionsConfig' => [
[
'label' => 'Редактировать',
'url' => '/clients/edit/{id}',
'icon' => 'fa-solid fa-pen',
'class' => 'btn-outline-primary',
'type' => 'edit',
],
[
'label' => 'Удалить',
'url' => '/clients/delete/{id}',
'icon' => 'fa-solid fa-trash',
'class' => 'btn-outline-danger',
'type' => 'delete',
'confirm' => 'Вы уверены, что хотите удалить этого клиента?',
],
],
'emptyMessage' => 'Клиентов пока нет',
'emptyIcon' => 'fa-solid fa-users',
'emptyActionUrl' => base_url('/clients/new'),
'emptyActionLabel' => 'Добавить клиента',
'emptyActionIcon' => 'fa-solid fa-plus',
'can_edit' => $this->access->canEdit('clients'),
'can_delete' => $this->access->canDelete('clients'),
];
}
}
```
### Параметры конфигурации
Параметр `id` задаёт уникальный идентификатор контейнера таблицы и используется для инициализации JavaScript-модуля. Параметр `url` определяет endpoint для AJAX-загрузки данных. Параметр `model` указывает экземпляр модели CodeIgniter, которая используется для запроса данных. Модель автоматически фильтруется по организации через трейт `TenantScopedModel` при его наличии.
Параметр `columns` описывает структуру колонок таблицы. Ключ массива соответствует имени поля в данных, а значение — ассоциативному массиву с параметрами отображения. Параметр `label` задаёт заголовок колонки, параметр `width` — ширину колонки в процентах или пикселях. Опционально можно указать `placeholder` для поля поиска, `searchTitle` для tooltip-подсказки и `align` для CSS-класса выравнивания содержимого.
Параметр `searchable` определяет массив имён колонок, по которым разрешён поиск. Эти колонки получат иконку поиска в заголовке. Параметр `sortable` определяет массив имён колонок, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю. Параметры `defaultSort` и `order` задают поле и направление сортировки по умолчанию.
### Методы контроллера для таблицы
Основной метод для отображения страницы с таблицей выглядит следующим образом:
```php
public function index()
{
// Проверка прав доступа
if (!$this->access->canView('clients')) {
return $this->forbiddenResponse('Нет прав для просмотра клиентов');
}
$config = $this->getTableConfig();
return $this->renderTwig('@Clients/index', [
'title' => 'Клиенты',
'tableHtml' => $this->renderTable($config),
'can_create' => $this->access->canCreate('clients'),
'can_edit' => $this->access->canEdit('clients'),
'can_delete' => $this->access->canDelete('clients'),
]);
}
```
Метод для AJAX-загрузки данных таблицы использует встроенную логику BaseController:
```php
public function table(?array $config = null, ?string $pageUrl = null)
{
// Проверка прав доступа
if (!$this->access->canView('clients')) {
return $this->forbiddenResponse('Нет прав для просмотра клиентов');
}
return parent::table($config, '/clients');
}
```
Метод `table()` автоматически определяет тип запроса (обычный или AJAX) и возвращает либо полную таблицу, либо только tbody и tfoot соответственно. Для определения типа запроса используется заголовок `X-Requested-With` или параметр `format=partial`.
---
## Подключение в шаблоне
### Базовое подключение
Для подключения таблицы в шаблоне Twig используется компонент `table.twig`. Компонент принимает данные из метода `renderTable()` контроллера, который возвращает полностью сформированный HTML:
```twig
{# app/Modules/Clients/Views/index.twig #}
{% extends 'layouts/base.twig' %}
{% block title %}Клиенты{% endblock %}
{% block content %}
{% endblock %}
{% block scripts %}
{{ parent() }}
{% endblock %}
```
### Прямое использование компонента
При необходимости таблицу можно подключить напрямую через `include`, передав все параметры вручную:
```twig
{% from '@components/table/macros.twig' import render_actions %}
{{ include('@components/table/table.twig', {
id: 'my-table',
url: '/my-module/table',
perPage: 25,
sort: sort|default(''),
order: order|default('asc'),
filters: filters|default({}),
items: items,
pagerDetails: pagerDetails,
columns: {
name: { label: 'Название', width: '40%' },
email: { label: 'Email', width: '30%' },
status: { label: 'Статус', width: '20%' },
},
actions: { label: 'Действия', width: '10%' },
actionsConfig: [
{ label: 'Ред.', url: '/edit/{id}', icon: 'fa-solid fa-pen', class: 'btn-outline-primary' },
],
can_edit: can_edit|default(true),
can_delete: can_delete|default(true),
emptyMessage: 'Записей не найдено',
emptyIcon: 'fa-solid fa-inbox',
emptyActionUrl: url('/create'),
emptyActionLabel: 'Создать',
emptyActionIcon: 'fa-solid fa-plus',
onRowClick: 'openClientDetails',
tableClass: 'table-sm',
}) }}
```
### Поддержка render_cell и render_actions
В шаблонах Twig доступны глобальные функции для рендеринга ячеек и действий. Функция `render_cell()` автоматически обрабатывает значение ячейки в зависимости от типа данных:
```twig
{# Ячейка с автоматическим форматированием #}
{{ render_cell(item, 'name')|raw }} |
{{ render_cell(item, 'email')|raw }} |
{{ render_cell(item, 'price')|raw }} |
{{ render_cell(item, 'created_at')|raw }} |
{# Ячейка с кастомным классом #}
{{ render_cell(item, 'status', { class: 'badge bg-success' })|raw }}
```
Функция `render_actions()` рендерит кнопки действий для строки таблицы:
```twig
{% set actions = [
{ label: 'Ред.', url: '/edit/' ~ item.id, icon: 'fa-solid fa-pen', class: 'btn-outline-primary' },
{ label: 'Удалить', url: '/delete/' ~ item.id, icon: 'fa-solid fa-trash', class: 'btn-outline-danger' },
] %}
{{ render_actions(actions)|raw }}
```
---
## Конфигурация колонок
### Параметры колонки
Каждая колонка описывается массивом с возможными параметрами. Обязательным параметром является только `label`, остальные опциональны:
```php
'columns' => [
'name' => [
'label' => 'Название',
'width' => '40%',
'placeholder' => 'Поиск по названию',
'searchTitle' => 'Нажмите для поиска',
'align' => 'text-start',
],
'price' => [
'label' => 'Цена',
'width' => '15%',
'align' => 'text-end',
],
'status' => [
'label' => 'Статус',
'width' => '15%',
],
]
```
Параметр `width` задаёт ширину колонки и может быть указан в процентах или пикселях. Рекомендуется использовать проценты для адаптивности или комбинировать фиксированные и относительные значения. Сумма ширин всех колонок обычно должна составлять 100% с учётом колонки действий.
### Поля searchable и sortable
Массив `searchable` определяет поля, по которым разрешён поиск. При указании поля в этом массиве в заголовке колонки появится иконка поиска, при клике на которую отобразится поле ввода:
```php
'searchable' => ['name', 'email', 'phone', 'company'],
```
Массив `sortable` определяет поля, по которым разрешена сортировка. При клике по заголовку сортируемой колонки таблица пересортируется по этому полю, при повторном клике направление сортировки меняется на противоположное:
```php
'sortable' => ['name', 'email', 'phone', 'created_at', 'price'],
```
Важно: имена полей в `searchable` и `sortable` должны соответствовать ключам массива `columns` и именам полей в базе данных.
---
## Конфигурация действий
### Структура actionsConfig
Параметр `actionsConfig` определяет кнопки действий для каждой строки таблицы. Каждое действие описывается массивом с параметрами:
```php
'actionsConfig' => [
[
'label' => '',
'url' => '/clients/edit/{id}',
'icon' => 'fa-solid fa-pen',
'class' => 'btn-outline-primary',
'title' => 'Редактировать',
'type' => 'edit',
'confirm' => null,
],
[
'label' => '',
'url' => '/clients/delete/{id}',
'icon' => 'fa-solid fa-trash',
'class' => 'btn-outline-danger',
'title' => 'Удалить',
'type' => 'delete',
'confirm' => 'Вы уверены?',
],
],
```
Параметр `url` поддерживает подстановку `{id}` для автоматической замены на идентификатор записи. Параметр `type` используется для фильтрации действий по правам доступа: действия с `type === 'edit'` показываются только при `can_edit === true`, действия с `type === 'delete'` — только при `can_delete === true`. Параметр `confirm` добавляет подтверждение действия через стандартный `confirm()` в JavaScript.
### Кастомные действия
Помимо типовых действий редактирования и удаления, можно определять кастомные действия:
```php
'actionsConfig' => [
[
'label' => 'Просмотр',
'url' => '/clients/view/{id}',
'icon' => 'fa-solid fa-eye',
'class' => 'btn-outline-secondary',
'title' => 'Просмотр клиента',
],
[
'label' => 'Создать сделку',
'url' => '/deals/create?client_id={id}',
'icon' => 'fa-solid fa-file-contract',
'class' => 'btn-outline-success',
'title' => 'Создать сделку',
],
[
'label' => 'Записать',
'url' => '/bookings/new?client_id={id}',
'icon' => 'fa-solid fa-calendar',
'class' => 'btn-outline-primary',
'title' => 'Запись на приём',
],
],
```
---
## Клиентская инициализация
### Базовая инициализация
JavaScript-модуль DataTable инициализируется для каждой таблицы на странице. При инициализации передаются параметры конфигурации:
```javascript
document.addEventListener('DOMContentLoaded', function() {
new DataTable('clients-table', {
url: '/clients/table',
perPage: 10,
debounceTime: 300,
preserveSearchOnSort: true
});
});
```
Параметр `url` задаёт endpoint для AJAX-загрузки данных. Параметр `perPage` определяет количество записей на странице по умолчанию. Параметр `debounceTime` задаёт задержку в миллисекундах перед выполнением поиска (защита от частых запросов при вводе). Параметр `preserveSearchOnSort` определяет, сохранять ли видимость полей поиска при сортировке.
### Методы DataTable
После инициализации экземпляр DataTable предоставляет методы для программного управления таблицей:
```javascript
const table = new DataTable('my-table', options);
// Установка фильтра
table.setFilter('name', 'Поисковый запрос');
// Установка количества записей на странице
table.setPerPage(25);
// Переход на конкретную страницу
table.goToPage(3);
// Перезагрузка данных
table.loadData();
```
---
## Пустое состояние и действия
### Конфигурация пустого состояния
При отсутствии данных в таблице отображается пустое состояние с возможностью действия. Параметры конфигурации:
```php
'emptyMessage' => 'Клиентов пока нет',
'emptyIcon' => 'fa-solid fa-users',
'emptyActionUrl' => base_url('/clients/new'),
'emptyActionLabel' => 'Добавить клиента',
'emptyActionIcon' => 'fa-solid fa-plus',
```
Параметр `emptyMessage` задаёт текст сообщения. Параметр `emptyIcon` указывает FontAwesome-иконку для отображения над сообщением. Параметры `emptyActionUrl`, `emptyActionLabel` и `emptyActionIcon` определяют кнопку действия при пустом состоянии.
### Условное скрытие действия
Кнопка действия при пустом состоянии отображается только если у пользователя есть право на создание:
```php
'emptyActionUrl' => $this->access->canCreate('clients')
? base_url('/clients/new')
: null,
'emptyActionLabel' => $this->access->canCreate('clients')
? 'Добавить клиента'
: null,
```
---
## Обработка special fieldMap
### Проблема несоответствия имён полей
При работе с моделями часто возникает ситуация, когда имя поля в базе данных отличается от имени свойства в Twig-шаблоне или имени параметра для фильтрации. Например, поле `client_name` в базе данных должно отображаться как «Клиент» и фильтроваться по параметру `client`. Для решения этой проблемы используется параметр `fieldMap`:
```php
protected function getTableConfig(): array
{
return [
'id' => 'deals-table',
'url' => '/deals/table',
'model' => $this->dealModel,
'columns' => [
'client_name' => ['label' => 'Клиент', 'width' => '30%'],
'title' => ['label' => 'Сделка', 'width' => '25%'],
'amount' => ['label' => 'Сумма', 'width' => '15%'],
'stage' => ['label' => 'Этап', 'width' => '15%'],
'created_at' => ['label' => 'Создан', 'width' => '15%'],
],
'searchable' => ['client_name', 'title', 'stage'],
'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'],
'defaultSort' => 'created_at',
'order' => 'desc',
// fieldMap для маппинга параметров фильтрации на реальные поля
'fieldMap' => [
'client' => 'client_name', // filters[client] -> client_name
'stage' => 'stage',
],
// ... остальные параметры
];
}
```
При использовании `fieldMap` параметры фильтрации из URL (`filters[client]`) автоматически маппятся на реальное поле базы данных (`client_name`). Это позволяет использовать понятные имена параметров в URL при сохранении корректных имён полей в запросе к базе данных.
---
## Кастомные scope для запросов
### Использование callable scope
Когда стандартной фильтрации недостаточно (например, нужны JOIN-ы с другими таблицами или сложные условия), можно использовать параметр `scope`. Это callable-функция, которая получает builder и полностью контролирует формирование запроса:
```php
protected function getTableConfig(): array
{
return [
'id' => 'deals-table',
'url' => '/deals/table',
'model' => $this->dealModel,
// ... columns, searchable, sortable и т.д.
// Кастомный scope для сложных запросов
'scope' => function($builder) {
$builder->resetQuery();
$builder->select('d.*, c.name as client_name, c.email as client_email')
->from('deals d')
->join('clients c', 'c.id = d.client_id', 'left')
->where('d.organization_id', session()->get('active_org_id'));
// Дополнительная фильтрация по статусу
$status = $this->request->getGet('filters[status]');
if ($status && $status !== 'all') {
$builder->where('d.status', $status);
}
// Фильтрация по диапазону дат
$dateFrom = $this->request->getGet('filters[date_from]');
$dateTo = $this->request->getGet('filters[date_to]');
if ($dateFrom) {
$builder->where('d.created_at >=', $dateFrom);
}
if ($dateTo) {
$builder->where('d.created_at <=', $dateTo . ' 23:59:59');
}
},
// fieldMap для JOIN-полей
'fieldMap' => [
'client' => 'c.name',
'client_email' => 'c.email',
],
];
}
```
При использовании `scope` параметр `model` игнорируется для построения запроса, и `scope` полностью контролирует SELECT, FROM, JOIN и WHERE. Параметры сортировки и фильтрации всё ещё применяются к builder после выполнения scope, поэтому в `fieldMap` нужно указывать полные имена полей с алиасами таблиц.
---
## Практические примеры
### Пример 1: Таблица клиентов
```php
class Clients extends BaseController
{
protected ClientModel $clientModel;
public function __construct()
{
$this->clientModel = new ClientModel();
}
public function index()
{
if (!$this->access->canView('clients')) {
return $this->forbiddenResponse('Нет прав для просмотра');
}
return $this->renderTwig('@Clients/index', [
'title' => 'Клиенты',
'tableHtml' => $this->renderTable($this->getTableConfig()),
'can_create' => $this->access->canCreate('clients'),
]);
}
public function table()
{
if (!$this->access->canView('clients')) {
return $this->forbiddenResponse('Нет прав');
}
return parent::table($this->getTableConfig(), '/clients');
}
protected function getTableConfig(): array
{
return [
'id' => 'clients-table',
'url' => '/clients/table',
'model' => $this->clientModel,
'columns' => [
'name' => ['label' => 'Имя / Название', 'width' => '35%'],
'email' => ['label' => 'Email', 'width' => '25%'],
'phone' => ['label' => 'Телефон', 'width' => '20%'],
'source' => ['label' => 'Источник', 'width' => '10%'],
'created_at' => ['label' => 'Создан', 'width' => '10%'],
],
'searchable' => ['name', 'email', 'phone'],
'sortable' => ['name', 'email', 'phone', 'source', 'created_at'],
'defaultSort' => 'created_at',
'order' => 'desc',
'actions' => ['label' => '', 'width' => '5%'],
'actionsConfig' => [
[
'label' => '',
'url' => '/clients/edit/{id}',
'icon' => 'fa-solid fa-pen',
'class' => 'btn-outline-primary btn-sm',
'title' => 'Редактировать',
'type' => 'edit',
],
[
'label' => '',
'url' => '/clients/delete/{id}',
'icon' => 'fa-solid fa-trash',
'class' => 'btn-outline-danger btn-sm',
'title' => 'Удалить',
'type' => 'delete',
'confirm' => 'Удалить клиента?',
],
],
'emptyMessage' => 'Клиентов пока нет',
'emptyIcon' => 'fa-solid fa-users',
'emptyActionUrl' => $this->access->canCreate('clients') ? '/clients/new' : null,
'emptyActionLabel' => $this->access->canCreate('clients') ? 'Добавить клиента' : null,
'emptyActionIcon' => 'fa-solid fa-plus',
'can_edit' => $this->access->canEdit('clients'),
'can_delete' => $this->access->canDelete('clients'),
];
}
}
```
### Пример 2: Таблица с кастомным рендерингом ячеек
```php
protected function getTableConfig(): array
{
return [
'id' => 'deals-table',
'url' => '/deals/table',
'model' => $this->dealModel,
'columns' => [
'client_name' => ['label' => 'Клиент', 'width' => '25%'],
'title' => ['label' => 'Сделка', 'width' => '25%'],
'amount' => ['label' => 'Сумма', 'width' => '15%'],
'stage' => ['label' => 'Этап', 'width' => '15%'],
'status' => ['label' => 'Статус', 'width' => '10%'],
'created_at' => ['label' => 'Создан', 'width' => '10%'],
],
'searchable' => ['client_name', 'title', 'stage'],
'sortable' => ['client_name', 'title', 'amount', 'stage', 'created_at'],
'defaultSort' => 'created_at',
'order' => 'desc',
'actions' => ['label' => '', 'width' => '5%'],
'actionsConfig' => [
[
'label' => '',
'url' => '/deals/edit/{id}',
'icon' => 'fa-solid fa-pen',
'class' => 'btn-outline-primary btn-sm',
'type' => 'edit',
],
],
'can_edit' => $this->access->canEdit('deals'),
];
}
```
В шаблоне Twig можно добавить кастомный рендеринг ячеек через Twig-фильтры:
```twig
{# В шаблоне ячейки с форматированием #}
{{ item.title }}
{% if item.description %}
{{ item.description|slice(0, 50) }}...
{% endif %}
|
{{ item.amount|number_format(0, ',', ' ') }} ₽
|
{% if item.status == 'active' %}
Активна
{% elseif item.status == 'won' %}
Выиграна
{% elseif item.status == 'lost' %}
Проиграна
{% endif %}
|
```
---
## Проверка при создании модуля
### Чек-лист при добавлении новой таблицы
При создании нового модуля с таблицей необходимо выполнить следующие действия:
**В контроллере:**
- Определить метод `getTableConfig()` с обязательными параметрами (`id`, `url`, `model`, `columns`)
- Указать `searchable` и `sortable` массивы с корректными именами полей
- Настроить `actionsConfig` с кнопками действий и проверкой прав
- Добавить метод `table()` для AJAX-загрузки данных
- Вызвать `parent::table()` для использования встроенной логики
- Проверить права доступа перед вызовом родительского метода
**В шаблоне:**
- Подключить DataTable.js в блоке `scripts`
- Инициализировать DataTable с корректным `id` и `url`
- Передать `tableHtml` из контроллера в шаблон
- Убедиться, что CSS стили таблицы подключены
**Модель:**
- Использовать трейт `TenantScopedModel` для автоматической фильтрации по организации
- Убедиться, что модель имеет поле `organization_id`
- Проверить, что модель возвращает данные в ожидаемом формате
---
## Типичные ошибки и их устранение
### Таблица не загружается
Если данные не загружаются, проверьте следующее:
- URL в конфигурации и при инициализации DataTable должны совпадать
- Метод `table()` контроллера должен вызывать `parent::table()`
- Модель должна использовать трейт `TenantScopedModel` или обрабатывать фильтрацию вручную
- Проверьте консоль браузера на наличие ошибок JavaScript
- Убедитесь, что CSRF-токен передаётся корректно
### Сортировка не работает
Если сортировка не работает:
- Поле должно быть указано в массиве `sortable`
- Имя поля в `sortable` должно соответствовать ключу в `columns` и имени поля в базе данных
- Для JOIN-запросов используйте алиасы таблиц в `sortable` (`c.name` вместо `client_name`)
### Поиск не работает
Если поиск не работает:
- Поле должно быть указано в массиве `searchable`
- При использовании JOIN проверьте `fieldMap` для маппинга параметров
- Убедитесь, что в контроллере используется метод `like()` для фильтрации
### Действия не отображаются
Если кнопки действий не отображаются:
- Проверьте `can_edit` и `can_delete` в конфигурации
- Убедитесь, что `type` действия соответствует проверяемому праву (`'edit'` или `'delete'`)
- Проверьте параметр `actions` в конфигурации (должен быть `{label: 'Действия'}` или `true`)
---
## Сводка параметров конфигурации
| Параметр | Тип | Обязательный | Описание |
|----------|-----|--------------|----------|
| `id` | string | Да | Идентификатор контейнера таблицы |
| `url` | string | Да | URL для AJAX-загрузки |
| `model` | Model | Да | Экземпляр модели CodeIgniter |
| `columns` | array | Да | Конфигурация колонок |
| `searchable` | array | Нет | Поля для поиска |
| `sortable` | array | Нет | Поля для сортировки |
| `defaultSort` | string | Нет | Поле сортировки по умолчанию |
| `order` | string | Нет | Направление сортировки по умолчанию |
| `actions` | array\|bool | Нет | Конфигурация колонки действий |
| `actionsConfig` | array | Нет | Кнопки действий |
| `emptyMessage` | string | Нет | Сообщение при отсутствии данных |
| `emptyIcon` | string | Нет | Иконка при пустом состоянии |
| `emptyActionUrl` | string | Нет | URL действия при пустом состоянии |
| `emptyActionLabel` | string | Нет | Текст кнопки действия |
| `can_edit` | bool | Нет | Разрешено ли редактирование |
| `can_delete` | bool | Нет | Разрешено ли удаление |
| `fieldMap` | array | Нет | Маппинг параметров фильтрации |
| `scope` | callable | Нет | Кастомный запрос к базе данных |