abordage/nova-table-card
A Laravel Nova card that displays model records as a compact, configurable table on your dashboard. Define the data source and columns to give users at-a-glance tabular summaries without leaving Nova.
1.x targets Nova 5; 0.2.x stays on Nova 4. Composer will tell you immediately if you guessed wrong:
composer require abordage/nova-table-card:^1.0
If you're still on Nova 4, pin ^0.2 and don't upgrade until the Nova upgrade lands.src/TableCard.php (the abstract base you extend — it defines the column/row contract, title, and width) and the README's example card. Everything else in the package is rendering glue.// app/Providers/NovaServiceProvider.php
public function dashboards()
{
return [
$this->dashboardUsing('main', [
new \App\Nova\Cards\LatestOrders,
]),
];
}
app/Nova/Cards/LatestOrders.php, extend TableCard, define the column labels and return the row data (an array or a mapped collection). Then hit /nova/dashboards/main and confirm it renders before adding a second card.composer.json before committing. If the constraint is nova: ^5.0, upgrading the card forces the Nova 5 upgrade; if it's ^4.0|^5.0 the bump is safe to take independently. Run the Nova upgrade first, then the card, so you're only debugging one variable.public function rows(): array
{
return Order::query()
->with('customer:id,name')
->latest()
->limit(10)
->get(['id', 'customer_id', 'total', 'status', 'created_at'])
->map(fn (Order $order) => [
'id' => $order->id,
'customer'=> $order->customer?->name ?? '—',
'total' => '$' . number_format($order->total / 100, 2),
'status' => Str::headline($order->status),
'placed' => $order->created_at->diffForHumans(),
])
->all();
}
Mapping to plain arrays (rather than ->toArray() on the model) is what keeps accessors and $appends from firing unnoticed.viewNova gate, which is coarse. If the card shows customer data, filter explicitly:
Order::query()->where('team_id', $request->user()->team_id)
For cards that shouldn't appear at all for some users, implement canSee() / authorizedToSee() on the card rather than returning empty rows — empty tables invite "is it broken?" tickets.Cache::remember('nova.orders-card', 60, fn () => ...) around the row builder keeps an aggregate query off the hot path.cards() method is the cheapest way to show "related records" (e.g. a customer's last five invoices) without writing a custom field or relationship panel — pass context via the card's constructor or resolve().$card = new LatestOrders();
$this->assertSame(['id', 'customer', 'total', 'status', 'placed'], array_keys($card->rows()[0]));
1.0.0 is a major bump for a one-line changelog. The only stated change is Nova 5 support, but semver majors exist to signal breakage. Diff the tag before upgrading a production app — the realistic candidates are a dropped Nova 4 constraint, a raised PHP/Laravel floor inherited from Nova 5, or a renamed method on TableCard. If you subclassed the card, check your overrides still match the parent signatures.->toArray() is a trap. Calling it on an Eloquent collection serializes dates as ISO strings, triggers $appends accessors (each potentially a query), and leaks any column you selected with *. Always narrow the select and map explicitly.canSee() when the count is zero.{!! !!}, you want a custom card or a resource index instead.vendor:publish for this package, re-publish and clear Nova's compiled assets (php artisan nova:publish, then a hard browser refresh) — stale JS from 0.2.x against Nova 5's frontend is the most likely source of a blank card.How can I help you explore Laravel packages today?