EN
Getting started

DataTable

DataTable builds a searchable, sortable table. You only write which records to list and which columns to show; DataTable draws the search box, the filters and the pagination.

DataTable is part of the PRO package. Add the package and its JavaScript file with the PRO steps on the Adding to an existing project page.

Quick example

A table is a Livewire class: query() says which records to list, columns() says which columns to show. Create app/Livewire/OrdersTable.php:

namespace App\Livewire;

use Acun\Ui\DataTable\Column;
use Acun\Ui\DataTable\DataTable;
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;

final class OrdersTable extends DataTable
{
    protected function query(): Builder
    {
        return Order::query();
    }

    protected function columns(): array
    {
        return [
            Column::make('Order no.', 'number')->searchable()->sortable(),
            Column::make('Customer', 'customer_name')->searchable('contains')->sortable(),
            Column::make('Order date', 'ordered_at')->dateTime()->sortable(),
        ];
    }
}

Put the table on a page, resources/views/admin/orders/index.blade.php:

@extends('layouts.admin.vertical')

@section('title', __('Orders'))

@section('content')
    <acun:page-header :title="__('Orders')" />

    <livewire:orders-table />
@endsection

Connect the page to a route in routes/web.php: Route::view('/orders', 'admin.orders.index')->name('orders.index');. When you open the page, the search box, the sort arrows and the page numbers are already there.

Columns

Every column starts with Column::make('Label', 'field'). The methods you chain after it set its format, e.g. Column::make('Order no.', 'number')->searchable()->sortable(). The common ones are below; for all of them and cell examples, see Columns.

Method What it does Example
searchable() The search box searches this column ->searchable('contains')
sortable() Clicking the header sorts by it ->sortable()
money(), date(), dateTime() Shows the value as money or a date ->money('TRY')
renderUsing() Your function produces the cell text ->renderUsing(fn (Order $order) => $order->formattedAmount())
view() Draws the cell with your own Blade file; the file gets the row as $row ->view('admin.tables.cells.order-status')
hidden() The column starts hidden; the column menu shows it ->hidden()
priority() Hides the column on narrow screens ->priority(3)

When a column is marked searchable(), a search box appears above the table. The search runs on a separate search column in the database. Add that column with a migration:

Schema::table('orders', function (Blueprint $table) {
    $table->searchColumn('number');          // number_search column and its index
    $table->searchColumn('customer_name');   // customer_name_search
});

Turkish letters and letter case do not matter: typing "ayse" finds "Ayşe". By default the start of the value is searched; to search anywhere in it, write searchable('contains'). Search modes and searching fields that are not shown as columns: Search. For the search infrastructure and large tables: the Search guide.

Filters

List the filters in the filters() method:

use Acun\Ui\DataTable\Filter;

protected function filters(): array
{
    return [
        Filter::multiSelect('Status', 'status')->options(OrderStatus::options())->inToolbar(),
        Filter::dateRange('Order date', 'ordered_at'),
    ];
}

A filter with inToolbar() is always visible under the search box. The others are in the drawer that the "Filters" button opens. Filter types: text, select, multiSelect, boolean, date, dateRange, number, numberRange. All of them and writing your own condition: Filters.

Sorting

sortable() makes the column header clickable. defaultSort() gives the order used until the user picks a column (details):

protected function defaultSort(): ?array
{
    return ['field' => 'ordered_at', 'direction' => 'desc'];
}

Row actions

To put buttons at the end of every row, write the actions() method:

use Acun\Ui\DataTable\Actions\RowAction;

protected function actions(): array
{
    return [
        RowAction::view(fn (Order $order) => route('orders.show', $order)),
        RowAction::edit(fn (Order $order) => route('orders.edit', $order))->can('update'),
        RowAction::make('ship')->label('Ship')->icon('truck')
            ->action(fn (Order $order) => $order->update(['status' => OrderStatus::Shipped])),
        RowAction::delete()->can('delete'),
    ];
}

view() and edit() are links. delete() asks for confirmation first, then deletes the record. make() is your own action. can('update') shows the button only to users who are allowed. All options: Actions.

Bulk actions

When the user selects rows, a selection bar opens above the table. Bulk actions live in this bar:

use Acun\Ui\DataTable\Actions\BulkAction;
use Illuminate\Database\Eloquent\Builder;

protected function bulkActions(): array
{
    return [
        BulkAction::make('ship')->label('Ship')->icon('truck')
            ->action(fn (Builder $query) => $query->update(['status' => OrderStatus::Shipped]))
            ->successMessage(':count orders shipped.'),
        BulkAction::delete()->authorizeRow(fn (Order $order) => auth()->user()->can('delete', $order)),
    ];
}

$query covers only the selected records. Every bulk action asks for confirmation before it runs. The header checkbox selects every record that matches the search, on every page. Selection rules and ready-made actions: Bulk actions.

Export

To let the user download the list, write the exportActions() method:

use Acun\Ui\DataTable\Actions\ExportAction;

protected function exportActions(): array
{
    return [ExportAction::csv(), ExportAction::json(), ExportAction::pdf(), ExportAction::print()];
}

An "Export" button appears in the toolbar. The user first picks the scope: this page, the filtered records, the selected ones or all records. The search, filters and sort order in the file match the table. Formats, scopes and queue settings: Export.

Note

The PDF button opens the print page; the user picks "Save as PDF" in the browser's print window. No extra package is needed. Files larger than 5,000 rows are built in the queue, so a queue worker (php artisan queue:work) must be running.

Pagination

Pagination comes built in: the user picks 10, 25, 50 or 100 rows, and the page opens with 25. perPageOptions() changes the options, and paginationMode() changes the pagination type for very large tables (Pagination). To change the starting size of one table, write defaultPerPage():

protected function defaultPerPage(): int
{
    return 50;
}

Authorization

authorizeTable() decides who can open the table, and query() decides which records they see:

protected function authorizeTable(): void
{
    $this->authorize('viewAny', Order::class);
}

authorizeTable() runs when the page opens and on every request after that. If users should only see their own records, put that condition in query(). Row and bulk actions also run only on those records.

Use can() on actions. A hidden or unauthorized action is not rendered, and the server rejects it even if the browser calls it. Give a row action's authorization rule to the bulk action too, with authorizeRow() (see the bulk delete example above). If even one selected record fails the rule, the action does not run at all. All the rules: Authorization and security.

Learn more

  • DataTable examples: seven working examples (scrolling table, fixed header, export…).
  • DataTable: all options: every method, the toolbar, appearance and configuration.
  • List pages: building the table by hand with your own components.
  • Search: search columns and indexes for large tables.
Acun UIDesigned for people.