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/Http/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 Table (rather than component) and
  2. then adding the required methods (query() and columns())

For example:

<?php

namespace App\Http\Livewire;

use Textstem\Models\Post;
use Textstem\Table\Column;
use Illuminate\Database\Eloquent\Builder;

class PostsTable extends Table
{
    // this is needed to identify which model is being used when building 
    // a layout using the table component
    public $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', 'Status')->component('columns.formatters.status'),
            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.

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. The enable the filter widget - define $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)
  • 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)

Comparing a column to the value of another column

To compare a column to the value of another column, use the 'where operation' 

Actions

Datatables have actions. To enable the actions widget - define $actionconfig. For example:

 public function mount()
    {
        $this->actionconfig = (new PageComponentActionBuilder())->collect();
        $this->filterconfig = (new PageComponentFilterBuilder())->collect();
        parent::mount();
    }