kdion4891 / laravel-livewire-tables

A dynamic, responsive Laravel Livewire table component with searching, sorting, checkboxes, and pagination.
302 stars 41 forks source link

Laravel Livewire Tables

Laravel Livewire Tables

A dynamic, responsive Laravel Livewire table component with searching, sorting, checkboxes, and pagination.

Installation

Make sure you've installed Laravel Livewire.

Installing this package via composer:

composer require kdion4891/laravel-livewire-tables

This package was designed to work well with Laravel frontend scaffolding.

If you're just doing scaffolding now, you'll need to add the Livewire @livewireScripts and @livewireStyles blade directives to your resources/views/layouts/app.blade.php file:

<!-- Styles -->
<link href="https://github.com/kdion4891/laravel-livewire-tables/blob/master/{{ asset('css/app.css') }}" rel="stylesheet">
@livewireStyles

...

<!-- Scripts -->
<script src="https://github.com/kdion4891/laravel-livewire-tables/raw/master/{{ asset('js/app.js') }}"></script>
@livewireScripts

This package also uses Font Awesome for icons. If you don't already have it installed, it's as simple as:

npm install @fortawesome/fontawesome-free

Then add the following line to resources/sass/app.scss:

@import '~@fortawesome/fontawesome-free/css/all.min.css';

Now all that's left is to compile the assets:

npm install && npm run dev

Making Table Components

Using the make command:

php artisan make:table UserTable --model=User

This creates your new table component in the app/Http/Livewire folder.

After making a component, you may want to edit the query and column methods:

class UserTable extends TableComponent
{
    public function query()
    {
        return User::query();
    }

    public function columns()
    {
        return [
            Column::make('ID')->searchable()->sortable(),
            Column::make('Created At')->searchable()->sortable(),
            Column::make('Updated At')->searchable()->sortable(),
        ];
    }
}

You don't have to use the render() method in your table component or worry about a component view, because the package handles that automatically.

Using Table Components

You use table components in views just like any other Livewire component:

@livewire('user-table')

Now all you have to do is update your table component class!

Table Component Properties

$table_class

Sets the CSS class names to use on the <table>. Defaults to table-hover.

Example:

public $table_class = 'table-hover table-striped';

Or, via .env to apply globally:

TABLE_CLASS="table-hover table-striped"

$thead_class

Sets the CSS class names to use on the <thead>. Defaults to thead-light.

Example:

public $thead_class = 'thead-dark';

Or, via .env to apply globally:

TABLE_THEAD_CLASS="thead-dark"

$header_view

Sets a custom view to use for the table header (displayed next to the search).

Example:

public $header_view = 'users.table-header';

Protip: any view you reference in your table component can use Livewire actions, triggers, etc!

{{-- resources/views/users/table-header.blade.php --}}
<button class="btn btn-primary" wire:click="createUser">Create User</button>

$footer_view

Sets a custom view to use for the table footer (displayed next to the pagination).

Example:

public $footer_view = 'users.table-footer';

$checkbox

Boolean for if the table should use checkboxes or not. Defaults to true.

Example:

public $checkbox = false;

Or, via .env to apply globally:

TABLE_CHECKBOX=false

$checkbox_side

The side of the table to place checkboxes on. Accepts left or right. Defaults to right.

Example:

public $checkbox_side = 'left';

Or, via .env to apply globally:

TABLE_CHECKBOX_SIDE="left"

$checkbox_attribute

Sets the attribute name to use for $checkbox_values. Defaults to id. I recommend keeping this as id.

Example:

public $checkbox_attribute = 'id';

$checkbox_values

Contains an array of checked values. For example, if $checkbox_attribute is set to id, this will contain an array of checked model ids. Then you can use those ids to do whatever you want in your component. For example, a deleteChecked button inside a custom $header_view.

Example deleteChecked button:

<button class="btn btn-danger" onclick="confirm('Are you sure?') || event.stopImmediatePropagation();" wire:click="deleteChecked">
    Delete Checked
</button>

Example deleteChecked method:

public function deleteChecked()
{
    Car::whereIn('id', $this->checkbox_values)->delete();
}

$sort_attribute

Sets the default attribute to sort by. Defaults to id. This also works with counts and relationships.

Example:

public $sort_attribute = 'created_at';

Count example (if you added ->withCount('relations') to the query() method):

public $sort_attribute = 'relations_count';

Relationship example (if you added ->with('relation') to the query() method):

public $sort_attribute = 'relation.name';

Notice the use of the dot notation. You use this when declaring column relationship attributes as well.

$sort_direction

Sets the default direction to sort by. Accepts asc or desc. Defaults to desc.

Example:

public $sort_direction = 'asc';

$per_page

Sets the amount of results to display per page. Defaults to 15.

Example:

public $per_page = 25;

Or, via .env to apply globally:

TABLE_PER_PAGE=25

Table Component Methods

query()

This method returns an Eloquent model query to be used by the table.

Example:

public function query()
{
    return Car::with('brand')->withCount('accidents');
}

columns()

This method returns an array of Columns to use in the table.

Example:

public function columns()
{
    return [
        Column::make('ID')->searchable()->sortable(),
        Column::make('Brand Name', 'brand.name')->searchable()->sortable(),
        Column::make('Name')->searchable()->sortable(),
        Column::make('Color')->searchable()->sortable()->view('cars.table-color'),
        Column::make('Accidents', 'accidents_count')->sortable(),
        Column::make()->view('cars.table-actions'),
    ];
}

Declaring Columns is similar to declaring Laravel Nova fields. Jump to the column declaration section to learn more.

thClass($attribute)

This method is used to compute the <th> CSS class for the table header.

$attribute

The column attribute.

Example:

public function thClass($attribute)
{
    if ($attribute == 'name') return 'font-italic';
    if ($attribute == 'accidents_count') return 'text-right';
    if ($attribute == 'brand.name') return 'font-weight-bold';

    return null;
}

trClass($model)

This method is used to compute the <tr> CSS class for the table row.

$model

The model instance for the table row.

Example:

public function trClass($model)
{
    if ($model->name == 'Silverado') return 'table-secondary';
    if ($model->accidents_count > 8) return 'table-danger';
    if ($model->brand->name == 'Ford') return 'table-primary';

    return null;
}

tdClass($attribute, $value)

This method is used to compute the <td> CSS class for the table data.

$attribute

The column attribute.

$value

The column value.

Example:

public function tdClass($attribute, $value)
{
    if ($attribute == 'name' && $value == 'Silverado') return 'table-secondary';
    if ($attribute == 'accidents_count' && $value < 2) return 'table-success';
    if ($attribute == 'brand.name' && $value == 'Ford') return 'table-primary';

    return null;
}

mount()

This method sets the initial table properties. If you have to override it, be sure to call $this->setTableProperties().

Example:

public function mount()
{
    $this->setTableProperties();

    // my custom code
}

render()

This method renders the table component view. If you have to override it, be sure to return $this->tableView().

Example:

public function render()
{
    // my custom code

    return $this->tableView();
}

Table Column Declaration

The Column class is used to declare your table columns.

public function columns()
{
    return [
        Column::make('ID')->searchable()->sortable(),
        Column::make('Created At')->searchable()->sortable(),
        Column::make('Updated At')->searchable()->sortable(),
    ];
}

make($heading = null, $attribute = null)

$heading

The heading to use for the table column, e.g. Created At. Can be null for view-only columns.

$attribute

The attribute to use for the table column value. If null, it will use a snake cased $heading.

You can also specify _counts and relationship attributes with a dot notation.

For counts, let's say I added withCount() to my query():

public function query()
{
    return Car::withCount('accidents');
}

Now I can create a column using this count like so:

Column::make('Accidents', 'accidents_count')->sortable(),

For relationships, let's say I added with() to my query():

public function query()
{
    return Car::with('brand');
}

Now I can create a column using any of the relationship attributes like so:

Column::make('Brand ID', 'brand.id')->searchable()->sortable(),
Column::make('Brand Name', 'brand.name')->searchable()->sortable(),

searchable()

Sets the column to be searchable.

sortable()

Sets the column to be sortable.

sortUsing($callback)

Allows custom logic to be used for sorting. Your supplied callable will receive the following parameters:

Additionally, your callback will be passed through Laravel's Container so you may inject any dependencies you need in your callback. Make sure your dependencies are listed before the parameters above.

Example:

Column::make('Paint Color')->searchable()->sortable()->sortUsing(function ($models, $sort_attribute, $sort_direction) {
    return $models->orderByRaw('?->\'$.color_code\' ?', [$sort_attribute, $sort_direction]);
});

This will sort the paint_color column using the JSON value color_code.

SQL Injection warning: Make sure if you are using any of Eloquent's *Raw methods, you always use the bindings feature.

view($view)

Sets a custom view to use for the column.

Example:

Column::make('Paint Color')->searchable()->sortable()->view('cars.table-paint-color'),

Notice how the column is still searchable() and sortable(), because the Car model contains a paint_color attribute!

If you're making a view-only column (for action buttons, etc), just don't make it searchable or sortable:

Column::make()->view('cars.table-actions'),

Custom column views are passed $model and $column objects, as well as variables passed from the table component.

For the Paint Color example, we can use the paint_color attribute from the model like so:

{{-- resources/views/cars/table-paint-color.blade.php --}}
<i class="fa fa-circle" style="color: {{ $model->paint_color }};"></i>

For the action buttons example, we can use the id attribute from the model like so:

{{-- resources/views/cars/table-actions.blade.php --}}
<button class="btn btn-primary" wire:click="showCar({{ $model->id }})">Show</button>
<button class="btn btn-primary" wire:click="editCar({{ $model->id }})">Edit</button>

Using a custom view for a relationship column? No problem:

{{-- resources/views/cars/table-brand-name.blade.php --}}
{{ $model->brand->name }}

Publishing Files

Publishing files is optional.

Publishing the table view files:

php artisan vendor:publish --tag=table-views

Publishing the config file:

php artisan vendor:publish --tag=table-config