
Elegant and testable state machines for Laravel applications β Define entity workflows declaratively (YAML), and control transitions with guards, actions, and events.
Laravel Statecraft est la solution élégante pour gérer les machines d'état dans vos applications Laravel. Définissez vos workflows de manière déclarative avec YAML, contrôlez les transitions avec des guards, des actions, et des événements.
β¨ Parfait pour : workflows de commandes, validation de contenu, gestion d'utilisateurs, processus d'approbation
π SimplicitΓ© : Configuration YAML intuitive + mΓ©thodes auto-gΓ©nΓ©rΓ©es
π§ FlexibilitΓ© : Guards complexes, actions personnalisΓ©es, historique des transitions
- π Declarative state machines for Eloquent models
- π‘οΈ Guard conditions with AND/OR/NOT logic expressions
- βοΈ Lifecycle actions on transitions
- π¦ Auto-generated methods like
canPublish()
andpublish()
- π§ͺ Built-in test support for transitions
- π Laravel event support (
Transitioning
,Transitioned
) - π§Ύ Optional transition history tracking
- βοΈ Comprehensive Artisan commands for YAML definitions and PHP classes
- π§ Configurable paths, events, and history tracking
- π― Dynamic resolution of guards and actions via Laravel container
- π§© Complex guard expressions with nested conditional logic
- π Export capabilities (JSON, Mermaid, Markdown)
- β Validation system for YAML definitions
- π Comprehensive documentation and examples
Install via Composer:
composer require grazulex/laravel-statecraft
Publish the configuration file and migrations:
# Publish configuration
php artisan vendor:publish --tag=statecraft-config
# Publish migrations (if using history tracking)
php artisan vendor:publish --tag=statecraft-migrations
php artisan migrate
The configuration file will be published to config/statecraft.php
where you can customize:
- State machine definitions path
- Default state field name
- Event system settings
- History tracking options
YAML Definition
state_machine:
name: OrderWorkflow
model: App\Models\Order
states: [draft, pending, approved, rejected]
initial: draft
transitions:
- from: draft
to: pending
guard: canSubmit
action: notifyReviewer
- from: pending
to: approved
guard: isManager
- from: pending
to: rejected
action: refundCustomer
- from: pending
to: approved
guard:
and:
- IsManager
- HasMinimumAmount
- from: pending
to: approved
guard:
or:
- IsManager
- IsVIP
- from: pending
to: approved
guard:
not: IsBlacklisted
- from: pending
to: approved
guard:
and:
- IsManager
- or:
- IsVIP
- IsUrgent
Key Features:
- π Backward Compatible - Simple string guards still work
- π― Dynamic Evaluation - Guards resolved at runtime
- π§© Nested Logic - Complex business rules supported
- π Event Integration - Expressions serialized in events and history
- β‘ Boolean Logic - AND/OR/NOT operations with short-circuit evaluation
Add the trait to your model:
use Grazulex\LaravelStatecraft\Traits\HasStateMachine;
class Order extends Model
{
use HasStateMachine;
protected function getStateMachineDefinitionName(): string
{
return 'order-workflow'; // YAML file name
}
}
$order = Order::find(1);
// Check if transitions are allowed
if ($order->canApprove()) {
$order->approve(); // Executes guard + action + state change
}
// Get current state and available transitions
$currentState = $order->getCurrentState();
$availableTransitions = $order->getAvailableTransitions();
use Grazulex\LaravelStatecraft\Traits\HasStateHistory;
class Order extends Model
{
use HasStateMachine, HasStateHistory;
// ... rest of your model
}
// Access transition history
$history = $order->stateHistory();
$lastTransition = $order->latestStateTransition();
class IsManager implements \Grazulex\LaravelStatecraft\Contracts\Guard
{
public function check(Model $model, string $from, string $to): bool
{
return auth()->user()?->is_manager;
}
}
class NotifyReviewer implements \Grazulex\LaravelStatecraft\Contracts\Action
{
public function execute(Model $model, string $from, string $to): void
{
Notification::route('mail', '[email protected]')
->notify(new OrderPendingNotification($model));
}
}
$order->stateHistory(); // β returns a collection of past transitions
php artisan statecraft:make order-workflow
php artisan statecraft:make article-status --states=draft,review,published --initial=draft
php artisan statecraft:generate database/state_machines/order-workflow.yaml
This generates:
- Guard classes in
app/StateMachines/Guards/
- Action classes in
app/StateMachines/Actions/
- Model examples in
app/StateMachines/
# List all YAML definitions
php artisan statecraft:list
# Show definition details
php artisan statecraft:show order-workflow
# Validate definitions
php artisan statecraft:validate --all
# Export to JSON, Mermaid, or Markdown
php artisan statecraft:export order-workflow json
php artisan statecraft:export order-workflow mermaid
php artisan statecraft:export order-workflow md --output=docs/workflow.md
statecraft:make supports additional options:
php artisan statecraft:make order-workflow --model=App\\Models\\Order --states=draft,pending,approved --initial=draft
statecraft:generate uses configurable output paths:
- Configure output directory via
statecraft.generated_code_path
- Defaults to
app/StateMachines/
if not configured
Use the built-in test utilities:
use Grazulex\LaravelStatecraft\Testing\StateMachineTester;
// Test transitions
StateMachineTester::assertTransitionAllowed($order, 'approved');
StateMachineTester::assertTransitionBlocked($order, 'rejected');
// Test states
StateMachineTester::assertInState($order, 'pending');
StateMachineTester::assertHasAvailableTransitions($order, ['approved', 'rejected']);
// Test methods
StateMachineTester::assertCanExecuteMethod($order, 'approve');
StateMachineTester::assertCannotExecuteMethod($order, 'reject');
Test complex guard expressions by setting up your models and authentication:
// Test AND logic with actual conditions
$manager = User::factory()->create(['is_manager' => true]);
$order = Order::factory()->create(['amount' => 1000]);
$this->actingAs($manager);
// Both conditions true: IsManager AND HasMinimumAmount
StateMachineTester::assertTransitionAllowed($order, 'approved');
// Make one condition false
$nonManager = User::factory()->create(['is_manager' => false]);
$this->actingAs($nonManager);
StateMachineTester::assertTransitionBlocked($order, 'approved');
// Test OR logic with different conditions
$vipOrder = Order::factory()->create(['is_vip' => true]);
StateMachineTester::assertTransitionAllowed($vipOrder, 'approved');
// Test NOT logic
$blacklistedOrder = Order::factory()->create(['customer_blacklisted' => true]);
StateMachineTester::assertTransitionBlocked($blacklistedOrder, 'approved');
Laravel Statecraft dispatches events during transitions:
use Grazulex\LaravelStatecraft\Events\StateTransitioning;
use Grazulex\LaravelStatecraft\Events\StateTransitioned;
// Listen to state changes
Event::listen(StateTransitioning::class, function ($event) {
// Before transition
$event->model; // The model
$event->from; // From state
$event->to; // To state
$event->guard; // Guard class (if any)
$event->action; // Action class (if any)
});
Event::listen(StateTransitioned::class, function ($event) {
// After transition
Log::info("Order {$event->model->id} transitioned from {$event->from} to {$event->to}");
});
For comprehensive documentation, examples, and advanced usage:
- Console Commands - Console commands reference
- Guards and Actions - Dynamic guards and actions
- Guard Expressions - AND/OR/NOT logic for guards
- Configuration - Configuration options
- Events - Event system usage
- Testing - Testing utilities
- History - State transition history
- Examples - Practical examples and use cases
- Quick Start: Check out the OrderWorkflow example
- Console Commands: Explore the console commands
- Guard Expressions: See guard-expressions-workflow.yaml for comprehensive examples
- Advanced Usage: Read the Guards and Actions documentation
- Configuration: Review the Configuration guide
- Testing: Learn about Testing utilities
Laravel-Statecraft is part of the Grazulex Tools ecosystem:
Laravel-Arc
(DTOs) β’ Laravel-Flowpipe
(Business Steps) β’ Laravel-Statecraft
(State Machines)
Designed for clean, testable, and modular Laravel applications.
JeanβMarc Strauven / @Grazulex
Blog: Open Source My Friend