Skip to content

Table

Arqel\Table\Table is the fluent builder behind a Resource's index page: columns, filters, sorting, pagination, search, and row/bulk/toolbar actions. It serializes to a schema in the Inertia payload, which @arqel-dev/ui's <DataTable> renders on the client.

You don't need to declare a table() method at all — Arqel derives a reasonable table straight from Resource::fields(). Reach for a custom Table when you need column types, filters, or behavior the auto-derivation can't express.

Full method reference: arqel-dev/table.

The minimum

php
use Arqel\Table\Table;
use Arqel\Table\Columns\TextColumn;

public function table(): Table
{
    return Table::make()
        ->columns([
            TextColumn::make('title')->sortable()->searchable(),
            TextColumn::make('author.name')->label('Author'),
        ]);
}

Column types

Columns follow the ADR-019 convention: concrete classes, not a factory alias (TextColumn::make('name'), not Column::text('name')).

ClassUse case
TextColumnDefault string/text
BadgeColumnStatus with colors
BooleanColumnCheckmark
DateColumnFormatted dates (date, dateTime, since)
NumberColumnRight-aligned numeric
IconColumnSingle icon
ImageColumnThumbnail
RelationshipColumnEager-loaded relation
ComputedColumnClosure-derived value
SelectColumnEditable select cell (inline edit)
TextInputColumnEditable inline text-input cell
ToggleColumnEditable toggle cell

Every column shares a common setter surface: label, sortable(bool), searchable(bool), hidden(bool), hiddenOnMobile(bool), align(string), width(string), tooltip(string|Closure).

php
use Arqel\Table\Columns\{BadgeColumn, DateColumn, RelationshipColumn};

->columns([
    BadgeColumn::make('status')->colors([
        'draft' => 'gray',
        'published' => 'green',
    ]),
    DateColumn::make('created_at')->since(),
    RelationshipColumn::make('category')->display('name'),
])

Filters

php
use Arqel\Table\Filters\{SelectFilter, DateRangeFilter, TernaryFilter};

public function table(): Table
{
    return Table::make()
        ->columns([...])
        ->filters([
            SelectFilter::make('status')->options([
                'draft' => 'Draft',
                'published' => 'Published',
            ]),
            DateRangeFilter::make('created_at'),
            TernaryFilter::make('is_featured'),
        ]);
}

Eight filter types are available: SelectFilter, MultiSelectFilter, DateRangeFilter, TextFilter, TernaryFilter, ScopeFilter (wraps an Eloquent scope), QueryBuilderFilter (a visual AND/OR condition tree), and TrashedFilter (soft-delete three-state: without/with/only).

Sort, search, pagination

php
Table::make()
    ->defaultSort('created_at', 'desc')
    ->perPage(25)
    ->perPageOptions([10, 25, 50, 100])
    ->searchable()   // global cross-column search
    ->selectable()   // adds checkboxes + enables bulk actions
    ->striped()
    ->compact();

Sorting is whitelisted server-side against columns marked sortable() — arbitrary column names in the request are ignored. per_page is validated against perPageOptions, and eager loading is inferred automatically from any RelationshipColumn you declare, via TableQueryBuilder.

Actions

Row, bulk, and toolbar actions attach directly to the table:

php
use Arqel\Actions\Actions;

Table::make()
    ->columns([...])
    ->actions([Actions::edit(), Actions::delete()])
    ->bulkActions([Actions::deleteBulk()])
    ->toolbarActions([Actions::create()]);

bulkActions() only take effect when selectable() is also set. See Actions for the full set of variants and how to write custom ones.

Empty state

php
Table::make()->emptyState([
    'icon' => 'inbox',
    'title' => 'No posts yet',
    'description' => 'Create your first post to get started.',
]);

Anti-patterns

  • Putting query scoping logic inside table() — use Resource::indexQuery() instead; Table only describes presentation.
  • Reaching for Column::make() out of habit — always use the concrete class (TextColumn::make, BadgeColumn::make, ...); the factory-alias convention is a Field-only thing (ADR-019).
  • Adding bulkActions() without selectable() — the checkboxes won't render and the actions become unreachable.

Next steps

MIT License — built with Inertia + React + Laravel.