Back to Course |
Filament 3 From Scratch: Practical Course

Table Actions: Row / Bulk / Header

In the process of while we're building simple tables, you got already familiar with three types of actions:

  • Edit/Delete buttons in a column
  • Create a button on top
  • Multi-checkbox Bulk Actions on top-left

In this lesson, we will look at how to create custom actions for all those cases and more.


Edit/Delete + Extra Buttons

Let's try to add another button to the Orders table, like "Mark as Complete," which would require confirmation and would change the status of the order.

Migration:

Schema::table('orders', function (Blueprint $table) {
$table->boolean('is_completed')->default(false);
});

Also, we make it fillable:

app/Models/Order.php:

class Order extends Model
{
use HasFactory;
 
protected $fillable = [
'user_id',
'product_id',
'price',
'is_completed',
];

Now, here's what action we can add to the table:

app/Filament/Resources/OrderResource.php:

return $table
->columns([
// ...
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('Mark Completed')
->requiresConfirmation()
->icon('heroicon-o-check-badge')
->hidden(fn (Order $record) => $record->is_completed)
->action(fn (Order $record) => $record->update(['is_completed' => true])),
])

Here's the visual result:

Pretty sure that the code above is self-explanatory, but here are a few things to notice:

  • make('Mark Completed') parameter means the label of the link
  • requiresConfirmation() will show the confirmation modal, like in the Delete action
  • icon can be chosen from the Blade Icons package
  • methods hidden() and action() both accept callback functions
  • hidden() should return true/false with the condition when the link should be hidden. In the screenshot above, you see one record as already completed

If you want to save some space in a table row, you may hide the actions under a vertical dots icon with ActionGroup():

return $table
->columns([
// ...
])
->actions([
Tables\Actions\ActionGroup::make([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('Mark Completed')
->requiresConfirmation()
->hidden(fn (Order $record) => $record->is_completed)
->icon('heroicon-o-check-badge')
->action(fn (Order $record) => $record->update(['is_completed' => true])),
])
])

This is how it looks now.

So this is how you add any custom action to your table "Actions column".


Table Action with Modal Edit Form

What if you want to have some custom mini-edit form: you click the link in the table, there's a modal form with a few fields (but not full edit), and you submit?

Let's implement it with "Mark as completed" but as a modal form with a checkbox. Not a very practical example, I know, but the goal is for you to understand the idea of how it works.

->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\Action::make('Change is completed')
->icon('heroicon-o-check-badge')
->fillForm(function (Order $order) {
return ['is_completed' => $order->is_completed];
})
->form([
Forms\Components\Checkbox::make('is_completed'),
])
->action(function (Order $order, array $data): void {
$order->update(['is_completed' => $data['is_completed']]);
}),

Here's the visual result:

As you can see, you need to fill in three things:

  • form fields
  • fill that form (it's not filled in automatically)
  • action to update the record after submit

Bulk Action: Add More to "Delete Selected"

By default, Filament shows a checkbox for each table record and allows to bulk-process them somehow. The only default function is "Bulk Delete", but you can add your own.

In the same table() method, there's a method bulkActions() where you pass the array of the Actions you want.

Let's try to implement "Bulk Mark as Completed".

Here's the code:

use Illuminate\Database\Eloquent\Collection;
 
// ...
 
return $table
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\BulkAction::make('Mark as Completed')
->icon('heroicon-o-check-badge')
->requiresConfirmation()
->action(fn (Collection $records) => $records->each->update(['is_completed' => true]))
->deselectRecordsAfterCompletion()
]),
])

Here's how it looks:

Instead of Action::make(), we have a BulkAction::make(), but other options are almost identical.

The difference in the action() method is that we work with Eloquent Collection and not one specific record.

Also, the method deselectRecordsAfterCompletion() makes sure that the selected checkboxes are de-selected after the action is processed.


Header Actions for All Table

There's also one more place where you can place Actions: on top of the table. It's called "Header Actions".

A pretty typical example would be to exporting records to Excel/CSV or if you just want to make other bulk actions more visible instead of hiding them under Bulk Actions.

Also, you can put a "Create new record" button there to make it more visible than the default top-right corner.

->headerActions([
Tables\Actions\Action::make('New Order')
->url(fn (): string => OrderResource::getUrl('create')),
])

Here's the button on top now: