Getting started
A workflow is a class that defines states and transitions. It is attached to a resource.
namespace App\Workflows;
use Anvil\Schema\Fields\TextInput;use Anvil\Workflow\Actor;use Anvil\Workflow\Escalation;use Anvil\Workflow\Definition;use Anvil\Workflow\State;use Anvil\Workflow\Transition;use Anvil\Workflow\Workflow;
final class OrderWorkflow extends Workflow{ public function key(): string { return 'order-approval'; }
public function version(): int { return 1; }
public function define(Definition $workflow): Definition { return $workflow ->column('approval_state') ->states([ State::make('draft')->label('Draft')->initial(), State::make('submitted')->label('Submitted')->duration(5) ->whenOverdue(Escalation::make()->remind()->run('reject', 'Not reviewed in time')), State::make('approved')->label('Approved')->locked(), State::make('rejected')->label('Rejected'), State::make('ordered')->label('Ordered')->locked()->final()->goal(), State::make('cancelled')->label('Cancelled')->final(), ]) ->transitions([ Transition::make('submit')->label('Submit')->from('draft', 'rejected')->to('submitted') ->by(Actor::creator()) ->guard('order.has-items') ->confirm('Submit the order for approval?'), Transition::make('approve')->label('Approve')->from('submitted')->to('approved') ->by(Actor::slot('approver', default: Actor::resolver('line-manager'))) ->fourEyes('submit'), Transition::make('reject')->label('Reject')->from('submitted')->to('rejected') ->by(Actor::sameAs('approve')) ->fourEyes('submit') ->reason(required: true), Transition::make('order')->label('Order')->from('approved')->to('ordered') ->by(Actor::role('Purchasing')) ->form([TextInput::make('order_number')->required()->maxLength(30)]) ->effect('order.send-to-supplier'), Transition::make('cancel')->label('Cancel')->from('draft', 'submitted', 'rejected')->to('cancelled') ->by(Actor::creator()) ->reason() ->optional() ->inbox(false), ]) ->defaultDuration(2) ->whenOverdue(Escalation::make()->notify(Actor::slot('escalation', default: Actor::role('Department heads')))); }}The workflow class
Section titled “The workflow class”| Method | Default | Purpose |
|---|---|---|
key() |
– (required) | unique key, [a-z][a-z0-9-]* (up to 64 characters); used in permissions (workflows.<key>.override), tables, commands and the API |
define(Definition) |
– (required) | the definition |
version() |
1 |
increase it whenever the definition changes, see Versions |
label() |
key, humanized | name in the panel and in notifications; may be a language key |
The definition
Section titled “The definition”Method of Definition |
Purpose |
|---|---|
column('approval_state') |
column of the record that holds the state (default status) |
states([...]) |
the states |
transitions([...]) |
the transitions |
defaultDuration(), deadline(), clockStartsIn(), expectedPath(), whenAtRisk(), whenOverdue() |
the schedule of the cases, see Durations and deadline |
required() |
always active in every tenant, without availability or activation |
fourEyesRelease() |
tenant settings need the release of a second administrator, see Tenant configuration |
Attaching it to a resource
Section titled “Attaching it to a resource”final class OrderResource extends Resource{ protected array $workflows = [OrderWorkflow::class];
public function collection(Collection $collection): Collection { $workflow = $this->workflow('order-approval');
return $collection ->columns([TextColumn::make('number'), $workflow->stateColumn(), $workflow->nextActorsColumn()]) ->filters([$workflow->stateFilter(), $workflow->myTasksFilter()]); }}A resource can have several workflows, each with its own state column. Transition names must be unique among the workflows of a resource. Columns and filters: Lists and detail page.
The state column
Section titled “The state column”The column (e.g. approval_state VARCHAR(40) NULL) must exist in the
table and must not be a field of the form: only transitions change
it. Add it with a migration:
$this->forge->addColumn('orders', [ 'approval_state' => ['type' => 'VARCHAR', 'constraint' => 40, 'null' => true],]);Records created in the panel or through the API start in the initial state; records created elsewhere start on their first transition, see Running transitions.
Checking the definition
Section titled “Checking the definition”php spark anvil:workflow:checkvalidates all definitions, stores new versions and reports records in unknown states; run it after every change and on deployment, see Commands.
Activating it
Section titled “Activating it”Tenants activate workflows on the page Workflows (Access::pages()),
after an operator made them available – or mark the definition
required() to have it active everywhere. See
Tenant configuration.