Datatable
To create a livewire datatable component for, for example, "Tags" - run the following command:
php artisan make:livewire Tables/TagsTable
This creates the two files we need to edit:
CLASS: app/Livewire/Tables/TagsTable.php
VIEW: resources/views/livewire/tables/tags-table.blade.php
The next step is to update the livewire component class by
- setting it to extend
Medialight\Textstem\Livewire\Table(rather thanLivewire\Component), - setting
$resourceName, and - adding the two required methods,
query()andcolumns().
For example:
<?php
namespace App\Livewire\Tables;
use Illuminate\Database\Eloquent\Builder;
use Medialight\Textstem\Livewire\Table;
use Medialight\Textstem\Models\Post;
use Medialight\Textstem\Table\Column;
class PostsTable extends Table
{
// this is needed to identify which model is being used when building
// a layout using the table component
public string $resourceName = 'posts';
public function query(): Builder
{
return Post::query();
}
public function columns(): array
{
return [
Column::make('title', 'Title'),
Column::make('post_type', 'Type'),
Column::make('post_status', 'Visibility')->component('columns.formatters.poststatus'),
Column::make('published', 'Published')->component('columns.formatters.yesno'),
Column::make('updated_at', 'Modified')->component('columns.formatters.timeago'),
];
}
}
You can delete the view blade file that was created unless you want to customise the table layout: the base class renders textstem::livewire.datasets.{viewName}, where $viewName is table by default (grid and tree are the other common choices).
Column formatters are the views in the package's resources/views/components/columns/formatters/, referred to as columns.formatters.{name}. Among them: text, html, yesno, enabled, date, datetime, timeago, modified, modified-by, publish-state, poststatus, page-status, thumbnail, thumbnail-small, category, user, roles, editbutton, editbuttons, viewbutton.
Other things the base class gives you
| Member | What it does |
|---|---|
$perPage |
Rows per page (default 20) |
$sortBy, $sortDirection |
Default sort (updated_at, desc); kept in the URL |
$search, $filters |
The search box and active filters; kept in the URL |
$searchColumns |
Columns the search box looks in when the model has no search scope |
$hasTrashed |
Set to true for soft-deleting models to offer a "show trashed" switch |
applyListQuery(Builder $query) |
Override to add your own scoping; data() and all_IDs() both go through it |
If you wish to cusomise the table layout, you can update the view blade file, looping through the columns to create the header, and then again for the table body like this:
<div>
<div class="relative overflow-x-auto ">
<table class="datatable">
<thead>
<tr>
@foreach($this->columns() as $column)
<x-columns.header
:column="$column"
:sortBy="$sortBy"
:sortDirection="$sortDirection" />
@endforeach
</tr>
</thead>
<tbody>
@foreach($this->data() as $row)
<tr class="bg-white border-b hover:bg-gray-50">
@foreach($this->columns() as $column)
<td>
<div class="">
<x-dynamic-component
:component="$column->component"
:value="$row[$column->key]"
>
</x-dynamic-component>
</div>
</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="my-4">
{{ $this->data()->links() }}
</div>
</div>
Filters
Datatables can have filters. To enable the filter widget, set $filterconfig.
For example, this can be done using a helper class (good for getting dynamic lists
such as a list pages)
class PageComponentsTable extends Table {
public function mount()
{
$this->filterconfig = (new PageComponentFilterBuilder())->collect();
parent::mount();
}
The easiest way to create a filter is to create a class that extends the FilterBuilder class. For example:
<?php
namespace Medialight\Textstem\Domain\Pages\Config;
use Medialight\Textstem\Contracts\CollectsFilterConfig;
use Medialight\Textstem\Filters\FilterBuilder;
class PageFilterBuilder extends FilterBuilder implements CollectsFilterConfig
{
public function collect()
{
$filters = [
'title' => [
'field' => 'title',
'input' => 'text',
'operations' => ['contains', 'starts with', 'is', 'is not'],
],
'tag' => [
'field' => 'tag',
'input' => 'text',
'model' => 'App\Models\GigListing',
],
'access' => [
'field' => 'access',
'input' => 'select',
'options' => ['public', 'private', 'protected'],
],
'template' => [
'field' => 'template',
'input' => 'select',
'options' => collect($this->listPageTemplates())->pluck('name')->toArray(),
],
'created_at' => [
'field' => 'created_at',
'input' => 'date',
'operations' => ['before', 'after'],
],
'updated_at' => [
'field' => 'updated_at',
'input' => 'date',
'operations' => ['before', 'after'],
],
];
return $this->prepare($filters);
}
}
For each filter, the following can be defined:
field- the field name to filter on (corresponds to a model attribute)input- the type of input to use (text, select, date)operations- the operations to allow (contains, starts with, is, is not, before, after, in, approximately, between)model- the model to be searched (if not defined, the model will be guessed from the field name)operators- accepted as another name foroperationsoptions- array of values to use for the select inputkoptions- array of value:labels to use for the select input (where the key is the option value, and the value is the option label)label- the label to use for the filter (if not defined, the label will be guessed from the field name)casts- the casts to use on the search value when filtering (eg 'filesize' will convert the value such as "2MB" to a number of bytes)
Actions
Datatables can have bulk actions for the selected rows. To enable the actions widget, set $actionconfig; see Actions. For example:
public function mount()
{
$this->actionconfig = (new PageComponentActionBuilder())->collect();
$this->filterconfig = (new PageComponentFilterBuilder())->collect();
parent::mount();
}