Компоненты

TableBuilder

Основы

TableBuilder - инструмент в MoonShine для создания настраиваемых таблиц для отображения данных. Он используется на индексной и детальной CRUD-страницах, а также для полей отношений, таких как HasMany, BelongsToMany, RelationRepeater и поля Json.

use MoonShine\UI\Components\Table\TableBuilder;
 
TableBuilder::make(iterable $fields = [], iterable $items = [])
<x-moonshine::table
:columns="[
'#', 'First', 'Last', 'Email'
]"
:values="[
[1, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[2, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[3, fake()->firstName(), fake()->lastName(), fake()->safeEmail()]
]"
/>

Основное использование

Пример использования TableBuilder:

TableBuilder::make()
->items([
['id' => 1, 'title' => 'Hello world']
])
->fields([
ID::make()->sortable(),
Text::make('Название', 'title'),
])

Основные методы

Поля

Поля для TableBuilder упрощают наполнение данными и отображение ячеек таблицы. По умолчанию поля выводятся в режиме preview. Метод fields определяет поля таблицы, каждое поле является ячейкой таблицы (td):

->fields([
ID::make()->sortable(),
Text::make('Название', 'title'),
])

Если необходимо указать атрибуты для td, воспользуйтесь методом customWrapperAttributes:

->fields([
ID::make()->sortable(),
Text::make('Название', 'title')->customWrapperAttributes(['class' => 'my-class']),
])

Данные

Метод items() устанавливает данные для таблицы:

->items($this->getCollection())

Пагинация

Метод paginator устанавливает пагинатор для таблицы. Необходимо передать объект, реализующий интерфейс MoonShine\Contracts\Core\Paginator\PaginatorContract:

Если необходимо указать пагинатор для QueryBuilder, можно воспользоваться встроенным ModelCaster, как в примере ниже:

->paginator(
(new ModelCaster(Article::class))
->paginatorCast(
Article::query()->paginate()
)
)

Пагинатор также можно указать через метод items().

Упрощенный вид пагинатора

Метод simple() применяет упрощенный стиль пагинации в таблице:

->simple()

Кнопки

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

->buttons([
ActionButton::make('Delete', fn() => route('name.delete')),
ActionButton::make('Edit', fn() => route('name.edit'))->showInDropdown(),
ActionButton::make('Go to home', fn() => route('home'))->blank()->canSee(fn($data) => $data->active),
ActionButton::make('Mass Delete', fn() => route('name.mass_delete'))->bulk(),
])

Для указания массовых действий над элементами таблицы необходимо у ActionButton указать метод bulk():

->buttons([
ActionButton::make('Mass Delete', fn() => route('name.mass_delete'))->bulk(),
])

Если вам необходимо зафиксировать кнопки (sticky), тогда воспользуйтесь методом stickyButtons():

->stickyButtons()

Отображение

Вертикальное отображение

Метод vertical() отображает таблицу в вертикальном формате (используется на DetailPage):

->vertical()

Если вы хотите изменить атрибуты колонок при вертикальном режиме, то воспользуйтесь параметрами title или value:

/** @param TableBuilder $component */
public function modifyDetailComponent(ComponentContract $component): ComponentContract
{
return $component->vertical(
title: fn(FieldContract $field, Column $default, TableBuilder $ctx): ComponentContract => $default->columnSpan(2),
value: fn(FieldContract $field, Column $default, TableBuilder $ctx): ComponentContract => $default->columnSpan(10),
);
}
  • title - Колонка с заголовком
  • value - Колонка со значением

Также можно передать целочисленное значение для указания колонок:

$component->vertical(
title: 2,
value: 10,
)

Редактируемая таблица

Метод editable() делает таблицу редактируемой, все поля переводятся в режим defaultMode (режим формы):

->editable()

Упрощенный режим

Метод preview() отключает отображение кнопок и сортировок для таблицы:

->preview()

С уведомлением "Ничего не найдено"

По умолчанию если у таблицы нет данных, то она будет пустой, но можно вывести сообщение "Пока записей нет". Для этого воспользуйтесь методом withNotFound():

TableBuilder::make()
->withNotFound()

Кастомизация строк

Поля ускоряют процесс и наполняют таблицу самостоятельно, выстраивая шапку таблицы с заголовками полей и сортировок, тело таблицы с выводом данных через поля и футер таблицы с массовыми действиями. Однако иногда может возникнуть потребность указать строки самостоятельно либо добавить дополнительные. Для этой задачи существуют методы для соответствующих секций таблицы: headRows (thead), rows (tbody), footRows (tfoot).

// tbody
TableBuilder::make()
->rows(
static fn(TableRowsContract $default) => $default->pushRow(
TableCells::make()->pushCell(
'td content'
)
)
)
 
// thead
TableBuilder::make()
->headRows(
static fn(TableRowContract $default) => TableRows::make([$default])->pushRow(
TableCells::make()->pushCell(
'td content'
)
)
)
 
// tfoot
TableBuilder::make()
->footRows(
static fn(?TableRowContract $default) => TableRows::make([$default])->pushRow(
TableCells::make()->pushCell(
'td content'
)
)
)

Обратите внимание, для footRows передается ?TableRowContract и в значении $default будет передано null, если кнопки массовых действий отсутствуют. Значение null можно указывать в списке $items в TableRows::make, оно будет проигнорировано.

TableRows и TableCells - это коллекции компонентов с дополнительным функционалом для быстрого добавления строки или ячейки таблицы.

TableRows::make()->pushRow(
TableCellsContract $cells,
int|string|null $key = null,
?Closure $builder = null
)
  • $cells - коллекция ячеек,
  • $key - уникальный ключ tr для массовых действий и событий обновления строк таблицы,
  • $builder - доступ к TableBuilder.
TableCells::make()->pushCell(
Closure|string $content,
?int $index = null,
?Closure $builder = null,
array $attributes = []
)
  • $content - содержимое ячейки,
  • $index - порядковый номер ячейки,
  • $builder - доступ к TableBuilder,
  • $attributes - HTML атрибуты ячейки.

У TableCells также есть дополнительные вспомогательные методы.

pushFields для быстрой генерации ячеек на основе полей:

TableCells::make()->pushFields(
FieldsContract $fields,
?Closure $builder = null,
int $startIndex = 0
)
  • $fields - коллекция полей,
  • $builder - доступ к TableBuilder,
  • $startIndex - начальный индекс (так как до этого, возможно, уже были добавлены ячейки таблицы)

Также доступны условные методы pushWhen и pushCellWhen.

Дополнительные возможности

Добавление новых строк

Метод creatable() позволяет добавлять новые строки, делает таблицу динамической:

->creatable(reindex: true, limit: 5, label: 'Добавить', icon: 'plus', attributes: ['class' => 'my-class'])
creatable(
bool $reindex = true,
?int $limit = null,
?string $label = null,
?string $icon = null,
array $attributes = [],
?ActionButtonContract $button = null
)
  • $reindex - режим редактирования с динамическим name,
  • $limit - количество записей, которые можно добавить,
  • $label - название кнопки,
  • $icon - иконка у кнопки,
  • $attributes - дополнительные атрибуты,
  • $button - кастомная кнопка добавления.

В режиме добавления необходимо, чтобы последний элемент был пустым (скелет новой записи)!

Если в таблице находятся поля в режиме редактирования с динамическим name, то нужно добавить метод или параметр reindex:

TableBuilder::make()
->creatable(reindex: true)
 
TableBuilder::make()
->creatable()
->reindex()

Пример с указанием кастомной кнопки добавления:

TableBuilder::make()
->creatable(
button: ActionButton::make('Foo', '#')
)

Переиндексация

Метод reindex() позволяет переиндексировать элементы таблицы, всем name атрибутам элементов формы будет добавлен индекс. Пример: Поле Text::make('Title', 'title') на первой строке tr таблицы будет иметь вид <input name="title[1]">. В режиме creatable или removable при добавлении/удалении новой строки все атрибуты name будут переиндексированы с учетом порядкового номера.

->reindex()

Сортировка перетаскиванием

Метод reorderable() добавляет возможность сортировки строк перетаскиванием:

->reorderable(url: '/reorder-url', key: 'id', group: 'group-name')
  • $url - URL-обработчика,
  • $key - ключ элемента,
  • $group - группировка (если требуется).

Метод sticky() делает заголовок таблицы фиксированным:

->sticky()

Выбор колонок

Метод columnSelection() добавляет возможность выбора отображаемых колонок:

->columnSelection()

Если необходимо у определенных полей отключить выбор отображения, то воспользуйтесь методом columnSelection у поля с параметром, равным false:

TableBuilder::make()
->fields([
Text::make('Title')
->columnSelection(false),
Text::make('Text')
])
->columnSelection()

При использовании columnSelection параметр name компонента TableBuilder должен быть уникальным для всех страниц. Это связано с тем, что данные сохраняются в localStorage на основе значения name компонента.

Метод searchable() добавляет функцию поиска по таблице:

->searchable()

Действие по клику

Метод clickAction() задает действие при клике на строку: В примере ниже при клике на строку таблицы произойдет клик на кнопку редактирования.

->clickAction(ClickAction::EDIT)

Если вы используете кастомные кнопки или переопределили кнопки по умолчанию, в таком случае также может потребоваться указать селектор кнопки:

->clickAction(ClickAction::EDIT, '.edit-button')

Типы ClickAction:

  • ClickAction::SELECT - выбор строки для массовых действий,
  • ClickAction::EDIT - переход в редактирование,
  • ClickAction::DETAIL - переход в детальный просмотр.

Сохранение состояния в URL

Метод pushState() сохраняет состояние таблицы в URL:

->pushState()

Модификация чекбокса массовых действий

Метод modifyRowCheckbox() позволяет модифицировать чекбокс массовых действий. Пример ниже демонстрирует выбор активного чекбокса по умолчанию:

->modifyRowCheckbox(
fn(Checkbox $checkbox, DataWrapperContract $data, TableBuilder $ctx) => $data->getKey() === 2 ? $checkbox->customAttributes(['checked' => true]) : $checkbox
)

Настройка атрибутов

TableBuilder предоставляет методы для настройки HTML-атрибутов:

->trAttributes(fn(?DataWrapperContract $data, int $row): array => ['class' => $row % 2 ? 'bg-gray-100' : ''])
->tdAttributes(fn(?DataWrapperContract $data, int $row, int $cell): array => ['class' => $cell === 0 ? 'font-bold' : ''])
->headAttributes(['class' => 'bg-blue-500 text-white'])
->bodyAttributes(['class' => 'text-sm'])
->footAttributes(['class' => 'bg-gray-200'])
->customAttributes(['class' => 'custom-table'])

Асинхронная загрузка

Метод async() настраивает асинхронную загрузку таблицы:

Метод async должен быть после метода name

->async(
Closure|string|null $url = null,
string|array|null $events = null,
?AsyncCallback $callback = null,
)
  • $url - URL асинхронного запроса (в ответе необходимо вернуть TableBuilder),
  • $events - события, которые будут вызваны после успешного ответа,
  • $callback - JS callback, который можно добавить как обертку ответа.

После успешного запроса можно вызвать события, добавив параметр events.

use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
 
TableBuilder::make()
->name('crud')
->async(events: [
AlpineJs::event(JsEvent::FORM_RESET, 'main-form'),
AlpineJs::event(JsEvent::TOAST, params: ['text' => 'Success', 'type' => 'success']),
])

Список событий для TableBuilder:

  • JsEvent::TABLE_UPDATED - обновление таблицы,
  • JsEvent::TABLE_REINDEX - реиндексация таблицы (см. reindex())
  • JsEvent::TABLE_ROW_UPDATED - обновление строки таблицы (AlpineJs::event(JsEvent::TABLE_ROW_UPDATED, "{component-name}-{row-id}"))

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

Все параметры метода async являются опциональными, и по умолчанию TableBuilder автоматически укажет URL на основе текущей страницы.

В процессе использования TableBuilder в режиме async может возникнуть задача, когда вы используете его вне админ-панели на страницах, не объявленных в системе MoonShine. Тогда вам потребуется указать собственный URL и реализовать ответ с HTML таблицей. Давайте рассмотрим пример реализации:

TableBuilder::make()->name('my-table')->async(route('undefined-page.component', [
'_namespace' => self::class,
'_component_name' => 'my-table'
]))

Controller

namespace App\MoonShine\Controllers;
 
use Illuminate\Contracts\View\View;
use MoonShine\Laravel\MoonShineRequest;
use MoonShine\Laravel\Http\Controllers\MoonShineController;
 
final class UndefinedPageController extends MoonShineController
{
public function component(MoonShineRequest $request): View
{
$page = app($request->input('_namespace'));
 
$component = $page->getComponents()->findByName(
$request->getComponentName()
);
 
return $component->render();
}
}

Lazy и whenAsync методы

Если вам необходимо отправить запрос на обновление компонента TableBuilder сразу при загрузке страницы, то нужно добавить метод lazy(). Также методы lazy() и whenAsync() в сочетании могут решить задачу ленивой загрузки данных или загрузки данных из внешнего источника.

TableBuilder::make()
->name('dashboard-table')
->fields([
ID::make(),
Slug::make('Slug'),
Text::make('Title'),
Preview::make('Image')->image()
])
->async()
->lazy()
->whenAsync(
fn(TableBuilder $table) => $table->items(
Http::get('https://jsonplaceholder.org/posts')->json()
)
),

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

ActionButton::make('Reload')
->async(events: [AlpineJs::event(JsEvent::TABLE_UPDATED, 'my-table')]),
 
TableBuilder::make()
->name('my-table')
->fields([
ID::make(),
Slug::make('Slug'),
Text::make('Title'),
Preview::make('Image')->image()
])
->async()
->lazy()
->whenAsync(
fn(TableBuilder $table) => $table->items(
Http::get('https://jsonplaceholder.org/posts')->json()
)
),
->withNotFound()

Приведение к типу

Если вы используете данные в таблице без cast, необходимо указать, что в ваших данных является ключом. В противном случае некоторые возможности, такие как bulk-операции, работать не будут.

Пример:

TableBuilder::make()
->castKeyName('id')
->name('my-table')
->fields([
ID::make(),
Text::make('Title')
])
->items([
['id' => 3,'title' => 'Hello world']
])
->buttons([
ActionButton::make('Mass Delete')
->bulk()
]),

Метод cast служит для приведения значений таблицы к определенному типу. Так как по умолчанию поля работают с примитивными типами:

use MoonShine\Laravel\TypeCasts\ModelCaster;
 
TableBuilder::make()
->cast(new ModelCaster(User::class))

В этом примере мы привели данные к формату модели User с использованием ModelCaster.

За более подробной информацией обратитесь к разделу TypeCasts.

Использование в blade

Основы

Стилизованные таблицы можно создавать с помощью компонента moonshine::table.

<x-moonshine::table
:columns="[
'#', 'First', 'Last', 'Email'
]"
:values="[
[1, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[2, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[3, fake()->firstName(), fake()->lastName(), fake()->safeEmail()]
]"
/>

Упрощенный вид

Параметр simple позволяет создавать упрощенного вида таблицы.

<x-moonshine::table
:simple="true"
:columns="[
'#', 'First', 'Last', 'Email'
]"
:values="[
[1, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[2, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[3, fake()->firstName(), fake()->lastName(), fake()->safeEmail()]
]"
/>

Фиксированный заголовок

Если в таблице содержится большое количество элементов, то можно зафиксировать шапку при прокрутке таблицы.

<x-moonshine::table
:sticky="true"
:columns="[
'#', 'First', 'Last', 'Email'
]"
:values="[
[1, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[2, fake()->firstName(), fake()->lastName(), fake()->safeEmail()],
[3, fake()->firstName(), fake()->lastName(), fake()->safeEmail()]
]"
/>

С уведомлением "Ничего не найдено"

Параметр notfound позволяет выводить сообщение при отсутствии элементов таблицы.

<x-moonshine::table
:columns="[
'#', 'First', 'Last', 'Email'
]"
:notfound="true"
/>

Для перевода или изменения текста уведомления необходимо задать параметр translates.

<x-moonshine::table
:columns="[
'#', 'First', 'Last', 'Email'
]"
:notfound="true"
:translates="['notfound' => __('moonshine.ui.notfound')]"
/>

Слоты

Таблицу можно сформировать с использованием слотов.

<x-moonshine::table>
<x-slot:thead class="text-center">
<th colspan="4">Header</th>
</x-slot:thead>
<x-slot:tbody>
<tr>
<th>1</th>
<th>{{ fake()->firstName() }}</th>
<th>{{ fake()->lastName() }}</th>
<th>{{ fake()->safeEmail() }}</th>
</tr>
<tr>
<th>2</th>
<th>{{ fake()->firstName() }}</th>
<th>{{ fake()->lastName() }}</th>
<th>{{ fake()->safeEmail() }}</th>
</tr>
<tr>
<th>3</th>
<th>{{ fake()->firstName() }}</th>
<th>{{ fake()->lastName() }}</th>
<th>{{ fake()->safeEmail() }}</th>
</tr>
</x-slot:tbody>
<x-slot:tfoot class="text-center">
<td colspan="4">Footer</td>
</x-slot:tfoot>
</x-moonshine::table>

Стилизация

Для стилизации таблицы есть предустановленные классы, которые можно использовать для tr / td.

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

  • bgc-purple
  • bgc-pink
  • bgc-blue
  • bgc-green
  • bgc-yellow
  • bgc-red
  • bgc-gray
  • bgc-primary
  • bgc-secondary
  • bgc-success
  • bgc-warning
  • bgc-error
  • bgc-info
<x-moonshine::table>
<x-slot:thead class="bgc-secondary text-center">
<th colspan="3">Header</th>
</x-slot:thead>
<x-slot:tbody>
<tr>
<th class="bgc-pink">{{ fake()->firstName() }}</th>
<th class="bgc-gray">{{ fake()->lastName() }}</th>
<th class="bgc-purple">{{ fake()->safeEmail() }}</th>
</tr>
<tr>
<th class="bgc-green">{{ fake()->firstName() }}</th>
<th class="bgc-red">{{ fake()->lastName() }}</th>
<th class="bgc-yellow">{{ fake()->safeEmail() }}</th>
</tr>
</x-slot:tbody>
</x-moonshine::table>

TableBuilder в MoonShine предоставляет широкий спектр возможностей для создания гибких и функциональных таблиц в административной панели.