bp/docs/DATATABLE.md

757 lines
38 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Компонент динамических таблиц 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 %}
<div class="page-header d-flex justify-content-between align-items-center mb-4">
<h1 class="page-title">Клиенты</h1>
{% if can_create %}
<a href="{{ url('/clients/new') }}" class="btn btn-primary">
<i class="fa-solid fa-plus me-2"></i>Добавить клиента
</a>
{% endif %}
</div>
<div class="card">
<div class="card-body p-0">
{{ tableHtml|raw }}
</div>
</div>
{% endblock %}
{% block scripts %}
{{ parent() }}
<script src="{{ url('/assets/js/modules/DataTable.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
new DataTable('clients-table', {
url: '/clients/table',
perPage: 10,
debounceTime: 300,
preserveSearchOnSort: true
});
});
</script>
{% endblock %}
```
### Прямое использование компонента
При необходимости таблицу можно подключить напрямую через `include`, передав все параметры вручную:
```twig
{% from '@components/table/macros.twig' import render_actions %}
<div class="table-responsive">
{{ 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',
}) }}
</div>
```
### Поддержка render_cell и render_actions
В шаблонах Twig доступны глобальные функции для рендеринга ячеек и действий. Функция `render_cell()` автоматически обрабатывает значение ячейки в зависимости от типа данных:
```twig
{# Ячейка с автоматическим форматированием #}
<td>{{ render_cell(item, 'name')|raw }}</td>
<td>{{ render_cell(item, 'email')|raw }}</td>
<td>{{ render_cell(item, 'price')|raw }}</td>
<td>{{ render_cell(item, 'created_at')|raw }}</td>
{# Ячейка с кастомным классом #}
{{ 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
{# В шаблоне ячейки с форматированием #}
<td>
<strong>{{ item.title }}</strong>
{% if item.description %}
<br><small class="text-muted">{{ item.description|slice(0, 50) }}...</small>
{% endif %}
</td>
<td class="text-end">
{{ item.amount|number_format(0, ',', ' ') }}
</td>
<td>
{% if item.status == 'active' %}
<span class="badge bg-success">Активна</span>
{% elseif item.status == 'won' %}
<span class="badge bg-primary">Выиграна</span>
{% elseif item.status == 'lost' %}
<span class="badge bg-danger">Проиграна</span>
{% endif %}
</td>
```
---
## Проверка при создании модуля
### Чек-лист при добавлении новой таблицы
При создании нового модуля с таблицей необходимо выполнить следующие действия:
**В контроллере:**
- Определить метод `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 | Нет | Кастомный запрос к базе данных |