Building blocks
Guards, effects, forms and resolvers used by several workflows, or chosen
in a tenant configuration, are registered by key in a BlockProvider:
use Anvil\Schema\Fields\DateInput;use Anvil\Workflow\BlockProvider;use Anvil\Workflow\Blocks;
final class OrderBlocks implements BlockProvider{ public function register(Blocks $blocks): void { $blocks->guard('order.has-items', static fn (array|object $order): bool => service('orders')->hasItems($order)); $blocks->effect('order.send-to-supplier', static fn (array|object $order, array $data) => service('orders')->send($order, $data['order_number'])); $blocks->form('order.delivery', static fn (): array => [DateInput::make('delivery_on')->required()]); $blocks->resolver('line-manager', new LineManager()); }}public array $workflowBlocks = [\App\Workflows\OrderBlocks::class];| Kind | Signature |
|---|---|
guard |
fn (array|object $record, WorkflowContext $context): bool |
effect |
fn (array|object $record, array $data, WorkflowContext $context): void |
form |
fn (): array – a list of fields |
resolver |
a WorkflowResolver, or fn (array|object $record, WorkflowContext $context): array of internal user ids |
Keys start with a lower case letter, then letters, digits, _, . and
- (up to 100 characters), e.g. order.has-items. Use a prefix per
module. Keys are stored in the definition: renaming a key needs a new
version of the workflows using it.
Inline closures
Section titled “Inline closures”Closures written directly in a definition are building blocks too: they
get a key of their workflow (inline:approve.guard.1). They are not
offered to tenants, and changing their code does not change the stored
definition – increase the version anyway when their behaviour changes.
Why keys
Section titled “Why keys”The engine runs the stored definition (data), not the PHP class.
Referencing code by key keeps definitions comparable between versions and
lets tenant administrators choose resolvers without writing code.
anvil:workflow:check reports keys that are not registered.