Skip to content

Column API

Column classes live in InertiaX\Table\Columns. Create a builder with ColumnClass::make(string $key, ?string $label = null).

key(string $key) Link to key
Set the source field key
label(?string $label) Link to label
Set the heading; an omitted label is derived from the key
relationship(string $relation, string $attribute) Link to relationship
Read a persisted scalar field through a single Eloquent BelongsTo relation
type(ColumnType|string $type) Link to type
Set a built-in type or namespaced custom type
sortable(bool $value = true) Link to sortable
Permit sorting
searchable(bool $value = true) Link to searchable
Include in global search
filterable(bool $value = true) Link to filterable
Permit automatic filter generation
copyable(bool $value = true) Link to copyable
Show the copy control
toggleable(bool $value = true) Link to toggleable
Include in the column picker
visible(bool $value = true) Link to visible
Set initial visibility
wrap(bool $value = true) Link to wrap
Wrap displayed content
truncate(?int $length) Link to truncate
Limit displayed text length; 0 disables truncation; null or clearTruncate() removes the builder override
filter($filter, ?string $label = null) Link to filter
Attach a filter object, builder, Filter class name, or key string; it queries this column and keeps its own key, or takes the column’s
meta(array|string $meta, mixed $value = null) Link to meta
Merge column metadata or set one metadata key

ColumnType lives in InertiaX\Table\Columns\Enums. For defaults, see Defaults. Clearing a truncation override restores inherited/default behavior; use truncate(0) to disable truncation even when the table sets a length. Hidden values are still sent to the browser; see Security.

TextColumn Link to TextColumn
Text value
NumberColumn Link to NumberColumn
numberCast(string $cast) defaults to float; accepts int, integer, real, float, double, decimal
BooleanColumn Link to BooleanColumn
trueLabel(string) / falseLabel(string) default to True / False; trueIcon(string) / falseIcon(string) set icons
DateColumn Link to DateColumn
format(string) defaults to Y-m-d
DateTimeColumn Link to DateTimeColumn
format(string) defaults to Y-m-d H:i:s
TimeColumn Link to TimeColumn
format(string) defaults to H:i:s
JsonColumn Link to JsonColumn
Preserves structured JSON; React formats its display
BadgeColumn Link to BadgeColumn
variant(Closure|array|string|Variant $variant, mixed $default = null) and icon(Closure|array|string $icon, mixed $default = null)

Date, datetime, and time columns also support timezone(?string) and translate(bool = true). The default timezone is the application timezone; translation is off by default and uses the application locale when enabled. Formatting does not mutate a model-owned Carbon value.

Integer casts preserve large integers as decimal strings; decimal preserves a string value. Invalid numeric values fail instead of becoming zero. Boolean labels and icons affect presentation; the cell value remains Boolean.

The built-in renderer styles primary, secondary, success, danger, warning, and info badge variants. These are also the cases in InertiaX\Table\Columns\Enums\Variant. Other variant strings are accepted but need application styling or a custom renderer.

NumberColumn::make('amount_cents', 'Amount')
->numberCast('int')
->currency('USD', divideBy: 100)
->locale('en-US');
NumberColumn::make('ratio')->numberCast('decimal')->decimals(3)->locale('fr-FR');
currency(string $currency, int $divideBy = 1) Link to currency
Three uppercase letters, such as USD or JPY; divideBy must be a power of ten from 1 through 1000000000
decimals(int $digits) Link to decimals
Exactly 0 through 20 fractional digits
locale(string $locale) Link to locale
Two- or three-letter language, optional four-letter script, optional two-letter or three-digit region: en, en-US, zh-Hans-CN, es-419

Calling any presentation method enables formatting. Without currency(), the style is decimal; locale defaults to en-US and the divisor to 1. Without decimals(), currency precision follows Intl.NumberFormat currency defaults; decimal formatting uses its default precision. Currency codes are syntax-checked, not checked against a bundled currency registry. Locale matching is case-insensitive; variants and extensions are unsupported. The renderer canonicalizes the locale and rejects locales unavailable in its runtime instead of falling back to an unrelated locale.

Formatting changes the built-in cell text, title and copy text together. It leaves the number cast, raw cell value and query units unchanged. Integer/decimal strings are scaled exactly before formatting; an earlier float conversion can already have lost precision. Null displays as empty; negative zero keeps its sign. Non-numeric transformer or decorator output fails with table and column context. Per-row currencies, exchange conversion and locale-aware filter input need application code.

A NumberColumn subclass can declare protected nullable $currency, $locale, $divideBy and $fractionDigits properties. Their defaults are null (unconfigured); inherited values apply until a fluent method overrides that option. currency() overrides both currency and divisor. Formatting alone enables no query capability. Label a filter for scaled cents Amount (cents), since 8419 is still its input value for $84.19.

For stored cents, use ->numberCast('int')->currency('USD', divideBy: 100) without a transformer that also divides by 100; combining them scales the amount twice. Use a transformer when you intend to change the serialized value itself.

@inertiax/table-core exports formatTableNumber(value, format) for custom renderers; NumberFormat belongs to @inertiax/table-protocol. To reuse one Intl.NumberFormat, call createTableNumberFormatter(format) and retain its returned function within a component or request. Bound any multi-format cache. Invalid format options and values throw; formatting does not negotiate the browser locale.

TextColumn::make('customer_name', 'Customer')
->relationship('customer', 'name')
->searchable()
->sortable()
->filterable();

Use relationship() with an Eloquent query source when a column represents one persisted field on a related model. The example uses the order model’s customer() relation and the customer’s name field. The column key stays customer_name in table state. Enable search, sorting and filtering separately; the binding alone does not enable them.

The displayed value and built-in comparisons use the same related database field, with the relation’s keys, ordinary where constraints and model scopes. That includes the comparisons of a filter attached with filter() and of a filter whose column() names this column. Missing or scoped-out related records produce null. PHP accessors, casts and withDefault() objects on the related model do not change that value. Column transformers still receive the root record; use a loaded relation and transformUsing() when you need accessor or default-object presentation.

Only standard, single-hop BelongsTo relations are supported. Custom foreign and owner keys and same-table relations work. The table’s row key must remain a root field. Related fields must be persisted scalars: JSON paths, computed properties and selected aliases require explicit queries or callbacks. Ordinary root joins and explicit root projections are supported when the root table keeps its original name and the selected fields include the row key. Root scopes must preserve added projections: use addSelect() instead of replacing the selection with select().

The helper rejects collection/custom sources, aliased root sources, cross-connection relations, other relation families, and relation queries with ordering, limits, offsets, grouping, having, unions, joins, custom selects, distinct or locks. Same-table relations additionally require simple column constraints; raw SQL, nested condition groups and subqueries need an explicit query. These declaration errors fail before retrieving table rows.

sortUsing() and searchUsing() retain their explicit overrides. Custom clause applyUsing() and filter applyClauseUsing() callbacks receive the column key as $column and own their query behavior; the helper does not rewrite custom SQL. Existing dotted keys such as customer.name retain their data-path behavior and do not declare a query binding. For caller-owned projections, see custom search and sorting.

NumberColumn::make('orders_count', 'Orders')
->count('orders')
->numberCast('int')
->sortable()
->filterable();
NumberColumn::make('paid_total', 'Paid total (cents)')
->sum('paidOrders', 'amount_cents')
->sortable()
->filterable();

Use count() or sum() with an Eloquent query source and a standard, single-hop HasMany relation. Here, paidOrders() can be an ordinary named relation such as return $this->orders()->where('paid', true);. Custom relation keys, ordinary where constraints and related model scopes apply to both the displayed aggregate and built-in comparisons. Each parent keeps one row and its original row key.

An empty count is 0. A sum is null when there are no matching rows or all matching values are null; it is not converted to zero. NumberColumn casts to float by default; the count example chooses int. Sum fields must be persisted numeric database fields. Related PHP accessors and casts do not change the aggregate.

The methods declare a value; enable sorting and filtering separately. Numeric aggregates do not participate in default global text search. Sorting uses the aggregate, then the root row key for ties; null placement follows the database. Built-in filters compare stored units: an amount_cents sum of 300 matches an equals filter value of 300, even when currency presentation displays $3.00. Use ->numberCast('int')->currency('USD', divideBy: 100) for stored cents; null sums still display as empty.

The query restrictions above also apply to aggregates, including the restrictions on same-table constraints and on relation ordering, limits and custom selects. Nested, polymorphic, through and pivot relations need explicit queries. sortUsing(), custom clause callbacks and applyClauseUsing() callbacks retain ownership of their queries. Existing withCount() / withSum() projections remain usable through those callbacks; a key such as orders_count alone never declares an aggregate or enables automatic comparison of a selected alias. The last relationship(), count() or sum() call on a builder selects its binding.

autoColumns() shows the fields the model serializes with toArray():

  1. the attributes each row carries: every column of the model’s table, or what a query selects, including selects that global scopes add and aliases such as withCount() counts; for a collection, the attributes its first model loaded;
  2. then the model’s $appends;
  3. limited to $visible when it is set, in the order $visible lists;
  4. without $hidden fields and the fields passed to except.

It always adds the row key, as a hidden column that is not toggleable or filterable, even when $visible leaves it out or $hidden or except names it. Appended accessors cannot be sorted, searched, or filtered, and neither can a query’s aliases or columns joined from another table. A raw select contributes only its final as alias, and another table’s * adds no field. When no field remains besides the row key, autoColumns() throws a ColumnDefinitionException. Explicit columns with the same key take precedence and appear after the inferred columns. Explicit AutoColumn::make($key) infers only that field and retains its chained options.

autoColumns(except: ['internal_notes']) leaves fields out of one table; in a Table class, set $defaultAutoColumnsExcept. autoColumns() without except keeps the list already set.

Inference needs an Eloquent model from the query or first collection row. Recognized model casts win over schema types. The array cast infers JsonColumn with both autoColumns() and AutoColumn::make(), preserving structured values, empty arrays, and nulls. An unrecognized cast falls back to text; without a cast, schema inference runs, followed by timestamp-name heuristics (_at), then text. Schema inference reads the database type, so a bigint unsigned column becomes a number column. On MySQL and SQLite, Laravel’s boolean() creates a tinyint(1) column, which becomes a boolean column and accepts only 0 and 1. On SQL Server it creates bit, which also becomes a boolean column. A MySQL or PostgreSQL bit(n) column stays text. Schema inspection failures are logged and use fallback inference. A query that selects * needs schema inspection to list the table’s columns: if inspection fails, $visible takes their place, and autoColumns() throws when $visible is empty.

Use explicit columns for parameterized or custom casts whose desired type is not inferred, and for plain array rows or empty collections. Review field exposure before using bulk inference.

transformUsing($transformer) appends a transformer; repeated calls do not replace earlier stages. mapAs(array $map, mixed $default = null) adds a mapping at priority 10.

addValueTransformer() Link to addValueTransformer
Parameters $transformer, ?int $priority = null, ?string $name = null
replaceValueTransformer() Link to replaceValueTransformer
Parameters string $name, $transformer
removeValueTransformer() Link to removeValueTransformer
Parameters string $name
clearValueTransformers() Link to clearValueTransformers
Parameters None; also removes built-in transformations

Transformers accept a closure, a ValueTransformer object, or its container-resolved class name. The contract’s transform() receives the value, record, column, and table. Returning null produces a null cell value. Transformations affect serialized cells, not query comparisons.

Lower priorities run first; the default priority is 100. Equal priorities retain declaration order. Replacing a named stage retains its priority and position. Duplicate names and missing replacement/removal targets fail.

decorateUsing($decorator) appends a decorator. Parallel methods are addCellDecorator($decorator, ?int $priority = null, ?string $name = null), replaceCellDecorator(string $name, $decorator), removeCellDecorator(string $name), and clearCellDecorators().

A closure or container-resolved CellDecorator receives the current CellPayload, record, column, and table. It may mutate or replace the payload; returning null retains the current payload. Ordering follows the same rules as transformers. Both contracts live in InertiaX\Table\Columns\Contracts.

Column meta() applies to every row; decorator metadata belongs to one cell. See Custom columns for an example.

For Eloquent and collections, sortUsing(?Closure $callback, DataSourceKind|string ...$sources) and searchUsing(?Closure $callback, DataSourceKind|string ...$sources) customize query behavior. Searchable columns whose type is not text require searchUsing(), including a TextColumn with a custom type. supportsSortSources(...$sources) and supportsSearchSources(...$sources) set the supported kinds separately. DataSourceKind is in InertiaX\Table\Enums. See Custom query behavior for arguments and return contracts.

Custom sources reject these callbacks. See Custom-source search for their searchable-column requirements.

See Callback context for injectable arguments and resolution rules.

View Markdown