Install & Publish:
composer require mhqady/flowra
php artisan vendor:publish --tag=flowra-config --tag=flowra-migrations --tag=flowra-stubs
php artisan migrate
Define a Workflow: Use the Artisan generator to scaffold a workflow:
php artisan make:flowra-workflow OrderWorkflow
This creates:
Workflow model classWorkflowService classAttach to a Model:
Add the HasWorkflow trait to your Eloquent model:
use Mhqady\Flowra\Traits\HasWorkflow;
class Order extends Model
{
use HasWorkflow;
protected $workflowClass = OrderWorkflow::class;
}
First Transition:
$order = Order::find(1);
$order->transition('approve'); // Moves from 'draft' to 'approved'
OrderWorkflow class in app/Flowra/Workflows.define() method in the workflow class.HasWorkflow trait is properly configured.Define states, transitions, and guards in the workflow class:
protected function define(): void
{
$this->state('draft')
->transition('approve')
->to('approved')
->guard(fn (Order $order) => $order->user->can('approve_orders'))
->action(fn (Order $order) => $order->notifyApproval())
->transition('reject')
->to('rejected')
->action(fn (Order $order) => $order->notifyRejection());
}
State-Based Logic:
if ($order->is('approved')) {
$order->fulfill();
}
Transition with Custom Guard:
$order->transition('approve', guard: new CustomApprovalGuard());
Bulk Transitions:
Order::where('status', 'pending')->transition('approve');
State Groups: Define groups in the workflow:
$this->stateGroup('review', ['draft', 'pending', 'approved']);
Query using:
Order::inStateGroup('review')->get();
WorkflowTransitioning/WorkflowTransitioned events for side effects.php artisan flowra:export to generate Mermaid diagrams.Flowra::fake() in PHPUnit.$workflow = Flowra::loadWorkflow('dynamic_workflow_id');
Migration Order:
flowra:migrate-workflow after publishing migrations.statuses table.State Naming:
active, created_at).Guard Failures:
WorkflowGuardFailedException. Catch and handle gracefully:
try {
$order->transition('approve');
} catch (WorkflowGuardFailedException $e) {
return back()->withError($e->getMessage());
}
Circular Dependencies:
A → B → A). Use ->unless() to block:
->unless(fn () => $order->wasRecentlyApproved())
Diagram Export:
php artisan flowra:export --workflow=OrderWorkflow --format=mermaid
Visualize workflows to spot misconfigurations.
Log Transitions:
Enable logging in config/flowra.php:
'log_transitions' => true,
SQL Queries:
Use DB::enableQueryLog() to inspect generated queries for state/registry operations.
Custom Actions:
Extend the WorkflowAction class or use closures:
->action(new SendEmailAction())
Dynamic Guards:
Implement WorkflowGuardContract for reusable logic:
class AgeGuard implements WorkflowGuardContract
{
public function pass($model, string $transition): bool
{
return $model->user->age >= 18;
}
}
State Metadata: Attach metadata to states for UI hints:
$this->state('draft')
->meta(['color' => 'yellow', 'icon' => 'edit']);
Workflow Events:
Listen for WorkflowLoaded, WorkflowSaved, or WorkflowDeleted events to sync external systems.
Default Workflow:
Set default_workflow in config/flowra.php to auto-attach workflows to models without explicit config.
Registry Retention:
Adjust registry_retention_days to limit history size (default: null = unlimited).
Concurrency:
Use transition() with lockForUpdate() for thread-safe operations:
$order = Order::lockForUpdate()->find($id);
$order->transition('approve');
How can I help you explore Laravel packages today?