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

  1. setting it to extend Medialight\Textstem\Livewire\Table (rather than Livewire\Component),
  2. setting $resourceName, and
  3. adding the two required methods, query() and columns().

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 for operations
  • options - array of values to use for the select input
  • koptions - 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();
    }
esc