ids array — and the table handles the selection plumbing.
How It Works
Actions are pure. They always accept the same parameters and operate on a single item. The table layer handles:- Mapping row model to action parameters via schema
ObjectPropertyauto-binding - Single-row execution (one action call)
- Bulk execution — synchronous loop per row for
asBulkAction(), single-call combined execution forasCombinedBulkAction(), background workflow for select-all
Registering Actions on a Table
Add actions to your table’scoreActions() method. Each action that should operate on the row model must declare an ObjectProperty in its schema() that matches the table’s row model type. Prefer configuring bulk behavior inside the action’s defaults() method so it stays attached when the action is later auto-wired through object actions or generated scaffolding. The table auto-binds the property:
app/Tables/OrderTable.php
schema() for an ObjectProperty whose model class matches the table row’s model. When found, that property is automatically bound to the row — no manual wiring needed.
app/Actions/Order/ApproveOrderAction.php
asBulkAction()
Marks an action as available for bulk selection. Without this, the action only appears as a row action. Prefer callingasBulkAction() from the action’s defaults() method instead of from the table. That keeps the action self-describing and lets later table wiring pick up the bulk behavior automatically.
asCombinedBulkAction()
Marks an action as bulk-only, but executes it once with the selected row IDs as anids array.
ids when exactly one row is selected, so your handler can always read $this->collect('ids').
Bulk Execution
The platform uses two distinct strategies depending on how items were selected:| Selection | Route | Behavior |
|---|---|---|
Explicit IDs + asBulkAction() | POST /actions/{action}/bulk | Synchronous loop — one action call per row |
Explicit IDs + asCombinedBulkAction() | POST /actions/{action}/execute | Single execute() call with ids array |
Select all (*) | POST /actions/{action}/bulk-table | BulkActionWorkflow dispatched in background |
Synchronous Execution
When the user selects explicit rows and clicks a bulk action, each item is processed in a sequential loop. On completion the user receives a flash message:- All succeeded —
":count items processed successfully." - Partial failure —
":succeeded of :total items processed. :failed failed."
$this->collect('ids'). The bulk endpoint also supports the same ids payload when you need to call it directly outside the table UI.
Background Workflow
“Select all” dispatches aBulkActionWorkflow. The workflow:
- Runs a
COUNTquery against the table’s current filter state to determine the total - Yields one
ProcessBulkTableChunkActivityper 100 records - Each activity reconstructs the table query, fetches its page via offset, and runs the action on each item
- Tracks progress via
StoredWorkflowcounters
Bulk Forms
When a bulk action requires additional input from the user — such as choosing a new status to apply to all selected orders — the table automatically presents a form dialog before executing. This uses the sameschema() and form() mechanism as single-item actions. No separate bulk form is needed.
Schema-Driven Bulk Forms
Any required, visible schema property beyond the modelObjectProperty triggers a form dialog in bulk context. The user fills in the extra fields, and those values are sent alongside the selected item IDs.
app/Actions/Order/SetOrderStatusAction.php
order property is hidden in the bulk form dialog (it’s injected server-side per item) — only the status field is shown.
For combined bulk actions, the selected row IDs are still sent once through bulkContext, so your handler can always work from ids even when the user selected a single row.
Custom Bulk Forms
Overrideform() to provide a custom React component. The same component handles both single-item and bulk execution — CoreActionForm detects the bulk context automatically via the bulkContext prop it receives:
resources/js/pages/orders/set-order-status-form.tsx
bulkContext is present, CoreActionForm routes the submission to the bulk endpoint, automatically excluding the model param (which is injected server-side per item). When it’s absent — such as when the action is used on a single row — submission proceeds normally.
Preconditions in Tables
When an action definespreconditions(), the table evaluates them per row. Actions that return hidden() are removed from that row entirely. Actions that return check() with unmet conditions show as disabled with a popover:
Performance
Preconditions run per row per action, but the platform optimizes aggressively:schema()anddefaults()are cached once per action class — not recomputed per rowform()is never called in table context — custom form components load on-demand when the user clicks- Actions without
preconditions()skip the check entirely (the platform detects this once per class)
ObjectProperty binding + preconditions() + hidden()/disabled() closures.
Auto-Modal for Incomplete Parameters
When a row action has schema properties beyond what the table auto-binds from the row model, clicking the row action opens a modal form automatically:Using Object Actions in Tables
When your model declaresobjectActions(), you can pull actions directly:
app/Tables/OrderTable.php
forTable() returns the actions ready for table use. The table maps the row’s model to the action’s ObjectProperty parameter automatically based on the model type.
Reference
Table Action Methods
| Method | Description |
|---|---|
asBulkAction(bool $selectAll = true) | Enable bulk selection. Pass false to disable “select all” |
asCombinedBulkAction() | Enable bulk selection with one execution and an ids array |
Next Steps
Actions
Action fundamentals: schemas, return values, preconditions, and object actions.
Data Tables
Table configuration, columns, filters, and sorting.