Events & hooks
Approval Flows fires domain events at every meaningful transition. Listen to them to run your own side effects — updating the record, posting to Slack, writing to a ledger — without touching the plugin.
The events
All live under Talivio\ApprovalFlows\Events:
| Event | Fired when | Payload |
|---|---|---|
ApprovalSubmitted |
a record is submitted for approval | $approval |
ApprovalStepPassed |
a step is satisfied and the run advances | $approval, $step |
ApprovalCompleted |
the final step passes; run is approved | $approval |
ApprovalRejected |
any approver rejects; run ends | $approval |
ApprovalEscalated |
a pending step crosses its escalation window | $approval, $step |
Each event exposes the Approval model. From there you can reach the underlying record via $approval->approvable, the flow via $approval->flow, and the audit log via $approval->actions.
Listening
use Talivio\ApprovalFlows\Events\ApprovalCompleted;
use Illuminate\Support\Facades\Event;
Event::listen(ApprovalCompleted::class, function (ApprovalCompleted $event) {
$record = $event->approval->approvable; // e.g. your PurchaseOrder
$record->update(['status' => 'approved']);
});
Or register a dedicated listener class in your AppServiceProvider / EventServiceProvider as usual.
A common pattern: mirror status onto the record
The plugin tracks approval state in its own tables and never mutates your model. If you want an approved/rejected column on the record itself, wire both outcomes:
Event::listen(ApprovalCompleted::class, fn ($e) =>
$e->approval->approvable->update(['status' => 'approved']));
Event::listen(ApprovalRejected::class, fn ($e) =>
$e->approval->approvable->update(['status' => 'rejected']));
Reading state without events
For synchronous checks you don't need events — the trait helpers query current state directly:
$record->approvalStatus(); // ApprovalStatus enum or null
$record->isPendingApproval(); // bool
$record->isApproved(); // bool
$record->latestApproval(); // Approval model
$record->approvals; // full history (MorphMany)
The audit trail
Every action is an ApprovalAction row with an ApprovalActionType (approved, rejected, delegated, commented, escalated, cancelled), the acting user_id, the approval_step_id, an optional comment, and timestamps. Build a timeline straight from $approval->actions (newest first).
Programmatic actions
Beyond submitForApproval(), drive the run through ApprovalManager:
$manager = app(\Talivio\ApprovalFlows\ApprovalManager::class);
$manager->approve($approval, $user, comment: 'LGTM');
$manager->reject($approval, $user, comment: 'Over budget');
$manager->delegate($approval, from: $user, to: $deputy);
$manager->cancel($approval, $user); // submitter only
Each guards its preconditions and throws an ApprovalException when the user may not act, the run isn't pending, or (for cancel) the caller isn't the submitter.
Next: Multi-tenancy.