Bulk actions
Bulk actions work on the selected records of the list.
use Anvil\Resource\BulkAction;
public function bulkActions(): array{ return [ BulkAction::make('markWon') ->label('Mark as won')->icon('trophy') ->permission('deals.close') // default: "<slug>.update" ->allowed(static fn (array $deal): bool => $deal['stage'] !== 'lost') // checked per record ->confirm('Mark the selected deals as won?') ->handle(function (array $deals): string { foreach ($deals as $deal) { $this->model()->update($this->recordKey($deal), ['stage' => 'won']); }
return count($deals) . ' deal(s) marked as won.'; }),
BulkAction::make('setStage') ->label('Set stage')->icon('signpost-split') ->form(static fn (Form $form): Form => $form->schema([ Radio::make('stage')->options(self::STAGES)->required(), ])) ->handle(function (array $deals, array $data): string { service('deals')->setStage($deals, $data['stage']);
return count($deals) . ' deal(s) updated.'; }), ];}Options
Section titled “Options”| Option | Effect | Default |
|---|---|---|
BulkAction::make('name') |
camelCase name, like record actions | |
label(), icon() |
button | name, none |
permission() |
required permission | <slug>.update |
allowed(fn ($record) => …) |
decides per record whether the action may change it | all |
confirm('…') |
confirmation question | none |
form(fn (Form $form) => …) |
dialog with fields; the closure gets no record | none |
maxRecords(500) |
largest selection | 500 |
handle(fn (array $records, array $data) => …) |
receives all selected records; returns a message or null |
Selection
Section titled “Selection”- The selection checkboxes are shown when the user may delete records or has the permission of at least one bulk action; the buttons appear when records are selected.
- The selection covers the records of the current page.
- Deleting a selection is built in and requires
<slug>.delete.
All or nothing
Section titled “All or nothing”- Every selected record is loaded through the resource (tenant,
scopeQuery()) and checked like a single action:allowed()and the permission including policies. - If one record is not found or not allowed, nothing is changed (404 / 403) – the user is not left with half a change.
- The handler runs once for the whole selection, in one transaction, and is audited. The list is then reloaded and the message shown.
For changes of thousands of records use a queued job and notify the user when it is done, see Notifications.