FilamentPHP Actions Generation Skill
Overview
This skill generates FilamentPHP v4 actions for resources, pages, and tables including modal actions, form actions, bulk operations, and custom workflows.
Documentation Reference
CRITICAL: Before generating actions, read:
- /home/mwguerra/projects/mwguerra/claude-code-plugins/filament-specialist/skills/filament-docs/references/actions/
Action Types
Standard CRUD Actions
use Filament\Actions;
// Create action Actions\CreateAction::make() ->label('New Post') ->icon('heroicon-o-plus');
// Edit action Actions\EditAction::make() ->label('Edit') ->icon('heroicon-o-pencil');
// View action Actions\ViewAction::make() ->label('View Details') ->icon('heroicon-o-eye');
// Delete action Actions\DeleteAction::make() ->requiresConfirmation() ->modalHeading('Delete post') ->modalDescription('Are you sure you want to delete this post? This action cannot be undone.') ->modalSubmitActionLabel('Yes, delete');
// Force delete (soft deletes) Actions\ForceDeleteAction::make() ->requiresConfirmation();
// Restore (soft deletes) Actions\RestoreAction::make();
Custom Actions with Modals
// Simple confirmation action Actions\Action::make('publish') ->label('Publish') ->icon('heroicon-o-check-circle') ->color('success') ->requiresConfirmation() ->modalHeading('Publish Post') ->modalDescription('Are you sure you want to publish this post?') ->modalSubmitActionLabel('Yes, publish') ->action(function (Model $record): void { $record->update(['status' => 'published', 'published_at' => now()]);
Notification::make()
->title('Post published')
->success()
->send();
});
// Action with form modal Actions\Action::make('send_email') ->label('Send Email') ->icon('heroicon-o-envelope') ->color('info') ->form([ Forms\Components\TextInput::make('subject') ->label('Subject') ->required() ->maxLength(255), Forms\Components\Select::make('template') ->label('Template') ->options([ 'welcome' => 'Welcome Email', 'reminder' => 'Reminder', 'promotion' => 'Promotion', ]) ->required(), Forms\Components\RichEditor::make('body') ->label('Message') ->required() ->columnSpanFull(), ]) ->action(function (Model $record, array $data): void { Mail::to($record->email)->send(new CustomEmail( subject: $data['subject'], template: $data['template'], body: $data['body'], ));
Notification::make()
->title('Email sent successfully')
->success()
->send();
});
// Wizard action (multi-step) Actions\Action::make('create_order') ->label('Create Order') ->icon('heroicon-o-shopping-cart') ->steps([ Forms\Components\Wizard\Step::make('Customer') ->schema([ Forms\Components\Select::make('customer_id') ->relationship('customer', 'name') ->required() ->searchable(), ]), Forms\Components\Wizard\Step::make('Products') ->schema([ Forms\Components\Repeater::make('items') ->schema([ Forms\Components\Select::make('product_id') ->relationship('product', 'name') ->required(), Forms\Components\TextInput::make('quantity') ->numeric() ->required() ->default(1), ]) ->columns(2), ]), Forms\Components\Wizard\Step::make('Shipping') ->schema([ Forms\Components\Textarea::make('address') ->required(), Forms\Components\Select::make('shipping_method') ->options([ 'standard' => 'Standard', 'express' => 'Express', ]), ]), ]) ->action(function (array $data): void { Order::create($data); });
Action Visibility and Authorization
Actions\Action::make('approve') // Visible only for specific status ->visible(fn (Model $record): bool => $record->status === 'pending')
// Hidden in certain conditions
->hidden(fn (Model $record): bool => $record->is_archived)
// Authorization check
->authorize('approve')
// Or with closure
->authorized(fn (): bool => auth()->user()->can('approve_posts'));
Actions with Side Effects
// Action that refreshes data Actions\Action::make('refresh') ->icon('heroicon-o-arrow-path') ->action(fn () => null) // No action needed ->after(fn ($livewire) => $livewire->dispatch('refresh'));
// Action with redirect Actions\Action::make('view_invoice') ->icon('heroicon-o-document') ->url(fn (Model $record): string => route('invoices.show', $record->invoice_id)) ->openUrlInNewTab();
// Action with download Actions\Action::make('download_pdf') ->icon('heroicon-o-arrow-down-tray') ->action(function (Model $record) { return response()->download( storage_path("invoices/{$record->invoice_id}.pdf") ); });
Table Row Actions
use Filament\Tables\Actions;
public static function table(Table $table): Table { return $table ->columns([...]) ->actions([ // Icon-only actions Actions\ActionGroup::make([ Actions\ViewAction::make(), Actions\EditAction::make(), Actions\DeleteAction::make(), ])->dropdownPlacement('bottom-end'),
// Or inline
Actions\ViewAction::make()
->iconButton(),
Actions\EditAction::make()
->iconButton(),
// Custom inline action
Actions\Action::make('duplicate')
->icon('heroicon-o-document-duplicate')
->iconButton()
->action(function (Model $record): void {
$replica = $record->replicate();
$replica->name = $record->name . ' (Copy)';
$replica->save();
}),
]);
}
Bulk Actions
use Filament\Tables\Actions;
->bulkActions([ Actions\BulkActionGroup::make([ // Standard bulk delete Actions\DeleteBulkAction::make(),
// Custom bulk action
Actions\BulkAction::make('publish')
->label('Publish Selected')
->icon('heroicon-o-check-circle')
->color('success')
->requiresConfirmation()
->action(function (Collection $records): void {
$records->each->update(['status' => 'published']);
Notification::make()
->title(count($records) . ' posts published')
->success()
->send();
})
->deselectRecordsAfterCompletion(),
// Bulk action with form
Actions\BulkAction::make('assign_category')
->label('Assign Category')
->icon('heroicon-o-tag')
->form([
Forms\Components\Select::make('category_id')
->label('Category')
->relationship('category', 'name')
->required(),
])
->action(function (Collection $records, array $data): void {
$records->each->update(['category_id' => $data['category_id']]);
}),
// Export bulk action
Actions\BulkAction::make('export')
->label('Export to CSV')
->icon('heroicon-o-arrow-down-tray')
->action(function (Collection $records) {
return Excel::download(
new RecordsExport($records),
'records.csv'
);
}),
]),
]);
Header Actions
use Filament\Tables\Actions;
->headerActions([ // Create action Actions\CreateAction::make() ->label('New Post'),
// Import action
Actions\Action::make('import')
->label('Import')
->icon('heroicon-o-arrow-up-tray')
->form([
Forms\Components\FileUpload::make('file')
->label('CSV File')
->acceptedFileTypes(['text/csv'])
->required(),
])
->action(function (array $data): void {
// Import logic
}),
// Attach action (for relationships)
Actions\AttachAction::make()
->preloadRecordSelect()
->recordSelectSearchColumns(['name', 'email']),
]);
Relation Manager Actions
// In RelationManager class
protected function getHeaderActions(): array { return [ Tables\Actions\CreateAction::make(), Tables\Actions\AttachAction::make() ->preloadRecordSelect() ->form(fn (Tables\Actions\AttachAction $action): array => [ $action->getRecordSelect(), Forms\Components\TextInput::make('role') ->required(), ]), Tables\Actions\AssociateAction::make(), ]; }
public function table(Table $table): Table { return $table ->columns([...]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DetachAction::make(), Tables\Actions\DissociateAction::make(), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DetachBulkAction::make(), Tables\Actions\DeleteBulkAction::make(), ]), ]); }
Page Actions
// In resource page classes
protected function getHeaderActions(): array { return [ Actions\EditAction::make(), Actions\DeleteAction::make(),
// Custom action
Actions\Action::make('preview')
->icon('heroicon-o-eye')
->url(fn (): string => route('posts.show', $this->record))
->openUrlInNewTab(),
];
}
// For List page protected function getHeaderActions(): array { return [ Actions\CreateAction::make(),
// Global action
Actions\Action::make('settings')
->icon('heroicon-o-cog')
->url(fn (): string => route('filament.admin.pages.settings')),
];
}
Action Styling and Configuration
Actions\Action::make('custom') // Label and icon ->label('Custom Action') ->icon('heroicon-o-star') ->iconPosition(IconPosition::After)
// Colors
->color('success') // primary, secondary, success, warning, danger, info, gray
// Size
->size(ActionSize::Large)
// Button style
->button()
->outlined()
->iconButton()
->link()
// Keyboard shortcut
->keyBindings(['mod+s'])
// Extra attributes
->extraAttributes([
'class' => 'my-custom-class',
'data-action' => 'custom',
])
// Badge
->badge(fn () => 5)
->badgeColor('danger')
// Tooltip
->tooltip('Click to perform action');
Notifications with Actions
use Filament\Notifications\Notification; use Filament\Notifications\Actions\Action;
Notification::make() ->title('Post created successfully') ->success() ->body('Your post has been created.') ->actions([ Action::make('view') ->button() ->url(route('posts.show', $record)), Action::make('undo') ->color('gray') ->action(fn () => $record->delete()), ]) ->send();
Output
Generated actions include:
-
Proper action type selection
-
Modal configurations
-
Form schemas for modal forms
-
Authorization checks
-
Notifications
-
Proper styling and icons