Компоненты

TableBuilder

Основы

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

 namespaces
use MoonShine\UI\Components\Table\TableBuilder;
 
TableBuilder::make(iterable $fields = [], iterable $items = [])
 namespaces
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()]
]"
/>
 
<x-moonshine::table>
<x-slot:thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Date</th>
<th>Status</th>
<th></th>
</tr>
</x-slot:thead>
 
<x-slot:tbody>
<tr>
<td>1</td>
<td>Ivan Ivanov</td>
<td>ivan@gmail.com</td>
<td>Editor</td>
<td>01.01.2025</td>
<td>
<x-moonshine::badge color="success">Active</x-moonshine::badge>
</td>
<td>
<x-moonshine::layout.flex justify-align="end" without-space class="gap-2">
<x-moonshine::link-button href="/" class="btn-square">
<x-moonshine::icon icon="eye"></x-moonshine::icon>
</x-moonshine::link-button>
 
<x-moonshine::link-button href="/" class="btn-square btn-secondary">
<x-moonshine::icon icon="pencil"></x-moonshine::icon>
</x-moonshine::link-button>
 
<x-moonshine::link-button href="/" class="btn-square btn-error">
<x-moonshine::icon icon="trash"></x-moonshine::icon>
</x-moonshine::link-button>
</x-moonshine::layout.flex>
 
</td>
</tr>
</x-slot:tbody>
<x-slot:tfoot></x-slot:tfoot>
</x-moonshien::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()]
]"
/>
 
<x-moonshine::table>
<x-slot:thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Date</th>
<th>Status</th>
<th></th>
</tr>
</x-slot:thead>
 
<x-slot:tbody>
<tr>
<td>1</td>
<td>Ivan Ivanov</td>
<td>ivan@gmail.com</td>
<td>Editor</td>
<td>01.01.2025</td>
<td>
<x-moonshine::badge color="success">Active</x-moonshine::badge>
</td>
<td>
<x-moonshine::layout.flex justify-align="end" without-space class="gap-2">
<x-moonshine::link-button href="/" class="btn-square">
<x-moonshine::icon icon="eye"></x-moonshine::icon>
</x-moonshine::link-button>
 
<x-moonshine::link-button href="/" class="btn-square btn-secondary">
<x-moonshine::icon icon="pencil"></x-moonshine::icon>
</x-moonshine::link-button>
 
<x-moonshine::link-button href="/" class="btn-square btn-error">
<x-moonshine::icon icon="trash"></x-moonshine::icon>
</x-moonshine::link-button>
</x-moonshine::layout.flex>
 
</td>
</tr>
</x-slot:tbody>
<x-slot:tfoot></x-slot:tfoot>
</x-moonshien::table>

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

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

TableBuilder::make()
->items([
['id' => 1, 'title' => 'Hello world']
])
->fields([
ID::make()->sortable(),
Text::make('Title'),
])
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'),
])
->fields([
ID::make()->sortable(),
Text::make('Title'),
])

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

->fields([
ID::make()->sortable(),
Text::make('Title')
->customWrapperAttributes(['class' => 'my-class']),
])
->fields([
ID::make()->sortable(),
Text::make('Title')
->customWrapperAttributes(['class' => 'my-class']),
])

Данные

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

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

Пагинация

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

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

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

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

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

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

->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(),
])
->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(),
])
->buttons([
ActionButton::make(
'Mass Delete',
fn() => route('name.mass_delete')
)->bulk(),
])

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

->stickyButtons()
->stickyButtons()

Отображение

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

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

->vertical()
->vertical()

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

vertical(
null|Closure|int $title = null,
null|Closure|int $value = null
)
vertical(
null|Closure|int $title = null,
null|Closure|int $value = null
)
  • 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),
);
}
/** @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),
);
}

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

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

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

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

->editable()
->editable()

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

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

->preview()
->preview()

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

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

->withNotFound()
->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')
)
)
// 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
)
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 = []
)
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
)
TableCells::make()->pushFields(
FieldsContract $fields,
?Closure $builder = null,
int $startIndex = 0
)
  • $fields - коллекция полей,
  • $builder - доступ к TableBuilder,
  • $startIndex - начальный индекс (так как до этого, возможно, уже были добавлены ячейки таблицы).

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

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

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

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

creatable(
bool $reindex = true,
?int $limit = null,
?string $label = null,
?string $icon = null,
array $attributes = [],
?ActionButtonContract $button = null
)
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 - кастомная кнопка добавления.
->creatable(
reindex: true,
limit: 5,
label: 'Add',
icon: 'plus',
attributes: ['class' => 'my-class']
)
->creatable(
reindex: true,
limit: 5,
label: 'Add',
icon: 'plus',
attributes: ['class' => 'my-class']
)

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

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

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

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

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

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

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

->reindex()
->reindex()

Без ключей

Метод withoutKey() заставляет таблицу использовать порядковые индексы вместо уникального ключа строки. Это полезно, когда источник данных может содержать повторяющиеся идентификаторы — например, при работе с BelongsToMany::deduplication(false) или любыми коллекциями, где одна и та же модель должна отображаться несколько раз.

->withoutKey()
->withoutKey()

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

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

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

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

->sticky()
->sticky()

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

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

columnSelection(null|string|Closure $prefix = null)
columnSelection(null|string|Closure $prefix = null)
->columnSelection()
->columnSelection()

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

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

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

Префикс для хранения

Параметр prefix позволяет добавить префикс к ключу хранения в localStorage. Это полезно, когда необходимо сохранять настройки выбора колонок для каждого пользователя индивидуально.

->columnSelection(prefix: auth()->id())
->columnSelection(prefix: auth()->id())

Также можно использовать замыкание:

->columnSelection(prefix: fn() => auth()->id())
->columnSelection(prefix: fn() => auth()->id())

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

->searchable()
->searchable()

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

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

->clickAction(ClickAction::EDIT)
->clickAction(ClickAction::EDIT)

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

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

Типы ClickAction:

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

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

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

->pushState()
->pushState()

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

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

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

Слоты

Вы можете добавить контент над таблицей слева или справа с помощью методов topLeft() и topRight().

TableBuilder::make()
// ...
->topLeft(function (): array {
return [];
})
->topRight(function (): array {
return [
Div::make([
// ...
])
];
})
TableBuilder::make()
// ...
->topLeft(function (): array {
return [];
})
->topRight(function (): array {
return [
Div::make([
// ...
])
];
})

Фильтры через FormBuilder

С помощью метода withFilters() вы можете добавить FormBuilder с полями для фильтрации данных в таблице. После отправки формы данные будут загружены заново.

->withFilters(formName: 'dashboard-form')
->withFilters(formName: 'dashboard-form')
  • $formName - Уникальное имя формы.

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

Пример с асинхронной загрузкой:

FormBuilder::make()
->name('dashboard-form')
->fields([
Date::make('Date')
])
->dispatchEvent([
AlpineJs::event(
JsEvent::TABLE_UPDATED, 'dashboard-table'
)
]),
 
TableBuilder::make()
->name('dashboard-table')
->withFilters('dashboard-form')
->async()
->fields([
Text::make('Title')->sortable()
])
->items([
['title' => fake()->word()]
])
FormBuilder::make()
->name('dashboard-form')
->fields([
Date::make('Date')
])
->dispatchEvent([
AlpineJs::event(
JsEvent::TABLE_UPDATED, 'dashboard-table'
)
]),
 
TableBuilder::make()
->name('dashboard-table')
->withFilters('dashboard-form')
->async()
->fields([
Text::make('Title')->sortable()
])
->items([
['title' => fake()->word()]
])

Префикс query-параметров

Вы можете задать префикс для query-параметров пагинации и сортировки. Это удобно, когда на странице используется несколько компонентов TableBuilder, и каждому нужны собственные параметры запроса, чтобы избежать конфликтов между ними.

->queryParamPrefix(prefix: 'users_')
->queryParamPrefix(prefix: 'users_')
  • $prefix - Строковый префикс.

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

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

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

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

 namespaces
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']),
])
 namespaces
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', ListRowEventParams::make($row_id))),
  • JsEvent::TABLE_ROW_ADDED - добавление новой клонированной строки,
  • JsEvent::TABLE_EMPTY_ROW_ADDED - добавление новой строки.

Обновление строки таблицы

Чтобы вызвать событие обновления отдельной строки таблица должна быть в режиме async и в таблице должно присутствовать поле ID.

Таблица на индексной странице ресурса в режиме async по умолчанию, если это поведение не отключено в ресурсе.

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

 namespaces
use MoonShine\Crud\JsonResponse;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\Support\EventParams\ListRowEventParams;
 
return JsonResponse::make()
->events([
AlpineJs::event(
JsEvent::TABLE_ROW_UPDATED,
'index-table-post-resource',
ListRowEventParams::make($post->id)
),
]);
 namespaces
use MoonShine\Crud\JsonResponse;
use MoonShine\Support\AlpineJs;
use MoonShine\Support\Enums\JsEvent;
use MoonShine\Support\EventParams\ListRowEventParams;
 
return JsonResponse::make()
->events([
AlpineJs::event(
JsEvent::TABLE_ROW_UPDATED,
'index-table-post-resource',
ListRowEventParams::make($post->id)
),
]);

через ListRowEventParams нужно обязательно указать идентификатор обновляемой строки.

Для получения дополнительной информации о 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'
])
)
TableBuilder::make()
->name('my-table')
->async(
route('undefined-page.component', [
'_namespace' => self::class,
'_component_name' => 'my-table'
])
)

Controller:

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

Lazy и whenAsync методы

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

Метод 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(),
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(),

Alpine.morph

По умолчанию при обновлении таблицы в режиме async() или lazy() содержимое DOM полностью заменяется. Это может привести к потере состояния Alpine.js-компонентов внутри таблицы (например, модальных окон или стилизованных Select-полей).

Метод withMorphLoad() включает режим морфинга DOM (через Alpine.morph), который обновляет содержимое таблицы (только то, что действительно изменилось), сохраняя состояние Alpine.js-компонентов.

TableBuilder::make()
->name('my-table')
->async()
->withMorphLoad()
TableBuilder::make()
->name('my-table')
->async()
->withMorphLoad()

Также методы 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()
)
),
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()
)
),

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

Если вы используете данные в таблице без 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()
]),
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() служит для приведения значений таблицы к определенному типу. По умолчанию поля работают с примитивными типами.

 namespaces
use MoonShine\Laravel\TypeCasts\ModelCaster;
 
TableBuilder::make()
->cast(new ModelCaster(User::class))
 namespaces
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()]
]"
/>
<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
: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()]
]"
/>
<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"
/>
<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
: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>
<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-primary bgc-secondary bgc-success bgc-warning bgc-error bgc-info bgc-purple bgc-pink bgc-blue bgc-green bgc-yellow bgc-red bgc-gray

<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>
<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>

Состояние загрузки

Для включения и выключения режима скелетона в таблице используйте метод sceleton().

->skeleton(true|false);
->skeleton(true|false);

Для включения и выключения режима спиннера в таблице используйте метод loader().

->loader(true|false);
->loader(true|false);

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