PHP code example of hpwebdeveloper / laravel-stateflow
1. Go to this page and download the library: Download hpwebdeveloper/laravel-stateflow library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
hpwebdeveloper / laravel-stateflow example snippets
// Check the current state
$order->state->name(); // 'pending'
$order->state->color(); // 'yellow'
// Get available transitions for the current user
$order->getNextStates(); // [Processing::class, Cancelled::class]
// Perform a transition with full audit trail
$order->transitionTo(Processing::class, reason: 'Order confirmed by warehouse');
// Legacy approach — manual state management everywhere
if ($order->status === 'pending') {
$order->update(['status' => 'processing']);
}
😐 Now answer these questions:
// 1. What states can this order transition to RIGHT NOW?
// 2. What buttons should I show the user in the UI?
// 3. Which users are ALLOWED to perform each transition?
// 4. Where is my complete workflow defined? (Check 10+ files?)
// 5. What's the audit trail for this state change?
// 6. How do I prevent invalid transitions like pending → shipped?
// 7. How do I store a reason or note explaining WHY this transition happened?
// 8. How do I query all orders "stuck" in a specific state?
// 9. What color/icon should represent each state in the UI?
// 10. How do I serialize state data consistently for my API/SPA?
// 11. How do I fire events when specific transitions occur?
// 12. How do I rollback or revert to a previous state if needed?
// 📋 app/Enums/BookingStateStatus.php — Complete topology in ONE place!
enum BookingStateStatus: string
{
case Draft = 'draft';
case Confirmed = 'confirmed';
case Paid = 'paid';
case Fulfilled = 'fulfilled';
case Cancelled = 'cancelled';
public function canTransitionTo(): array
{
return match ($this) {
self::Draft => [Confirmed::class],
self::Confirmed => [Paid::class, Cancelled::class],
self::Paid => [Fulfilled::class, Cancelled::class],
self::Fulfilled, self::Cancelled => [], // Final states
};
}
}
// 🎯 app/Models/Booking.php — Clean, 3-line configuration!
class Booking extends Model implements HasStatesContract
{
use HasStates;
public static function registerStates(): void
{
static::addState('state', StateConfig::make(BookingState::class)
->default(Draft::class)
->registerStates(BookingStateStatus::stateClasses())
->allowTransitionsFromArray(BookingStateStatus::transitions())
);
}
}
// In a migration file
Schema::table('orders', function (Blueprint $table) {
$table->string('state')->default('pending');
});
// app/States/Order/Pending.php
use Hpwebdeveloper\LaravelStateflow\Attributes\DefaultState;
use Hpwebdeveloper\LaravelStateflow\Attributes\StateMetadata;
use Hpwebdeveloper\LaravelStateflow\Attributes\AllowTransition;
#[DefaultState]
#[StateMetadata(title: 'Pending', description: 'Order is pending confirmation')]
#[AllowTransition(to: Processing::class)]
#[AllowTransition(to: Cancelled::class)]
class Pending extends OrderState
{
public const NAME = 'pending';
public static function color(): string { return 'yellow'; }
public static function icon(): string { return 'clock'; }
}
// app/Enums/BookingStateStatus.php
enum BookingStateStatus: string
{
case Draft = 'draft';
case Confirmed = 'confirmed';
case Paid = 'paid';
case Fulfilled = 'fulfilled';
case Cancelled = 'cancelled';
case Expired = 'expired';
/**
* 📋 Complete workflow topology at a glance!
*/
public function canTransitionTo(): array
{
return match ($this) {
self::Draft => [Confirmed::class, Expired::class],
self::Confirmed => [Paid::class, Cancelled::class, Expired::class],
self::Paid => [Fulfilled::class, Cancelled::class],
// Final states — no transitions
self::Fulfilled, self::Cancelled, self::Expired => [],
};
}
public function stateClass(): string { /* maps to state class */ }
public static function stateClasses(): array { /* all state classes */ }
public static function transitions(): array { /* for StateConfig */ }
}
// app/States/Booking/Draft.php
#[DefaultState]
#[StateMetadata(title: 'Draft', description: 'Booking in draft')]
class Draft extends BookingState
{
public const NAME = 'draft';
public static function color(): string { return 'gray'; }
}
use Hpwebdeveloper\LaravelStateflow\HasStates;
use Hpwebdeveloper\LaravelStateflow\HasStatesContract;
use Hpwebdeveloper\LaravelStateflow\StateConfig;
class Order extends Model implements HasStatesContract
{
use HasStates;
public static function registerStates(): void
{
static::addState('state', StateConfig::make(OrderState::class)
->default(Pending::class)
->registerStates([
Pending::class,
Processing::class,
Shipped::class,
Delivered::class,
Cancelled::class,
])
->allowTransition(Pending::class, Processing::class)
->allowTransition(Pending::class, Cancelled::class)
->allowTransition(Processing::class, Shipped::class)
->allowTransition(Processing::class, Cancelled::class)
->allowTransition(Shipped::class, Delivered::class)
);
}
}
use App\Enums\BookingStateStatus;
use App\States\Booking\{BookingState, Draft};
class Booking extends Model implements HasStatesContract
{
use HasStates;
public static function registerStates(): void
{
static::addState('state', StateConfig::make(BookingState::class)
->default(Draft::class)
->registerStates(BookingStateStatus::stateClasses())
->allowTransitionsFromArray(BookingStateStatus::transitions())
);
}
}
// Creating with a state class
$order = Order::create([
'state' => Processing::class, // Stored as 'processing' in DB
]);
// The package handles serialization automatically
$order->state; // Returns Processing instance
$order->state->name(); // 'processing'
// Get all states for the model (grouped by field)
Order::getStates();
// Returns: ['state' => ['pending', 'processing', 'shipped', 'delivered', 'cancelled']]
// Get states for a specific field
Order::getStatesFor('state');
// Returns: ['pending', 'processing', 'shipped', 'delivered', 'cancelled']
// Get default states
Order::getDefaultStates();
// Returns: ['state' => 'pending']
// Get default for specific field
Order::getDefaultStateFor('state');
// Returns: 'pending'
// Get state classes you can transition to from current state
$order->getNextStates();
// Returns: [Processing::class, Cancelled::class] (when in pending state)
// Count available transitions
count($order->getNextStates());
// Returns: 2
// Check if any transitions available
$order->hasNextStates();
// Returns: true
// Check specific transition
$order->canTransitionTo(Processing::class);
// Returns: true
$result = $order->transitionTo('processing');
if ($result->succeeded()) {
// Transition completed
}
if ($result->failed()) {
echo $result->error; // Error message
}
// Role-based: Define permitted roles in state class
#[StatePermission(roles: ['admin', 'warehouse'])]
class Shipped extends OrderState {}
// Policy-based: Complex authorization logic
class OrderPolicy {
public function transitionToShipped(User $user, Order $order): bool {
return $user->hasRole('warehouse') && $order->isPaid();
}
}
// Check permissions
$order->userCanTransitionTo($user, 'shipped');
use Hpwebdeveloper\LaravelStateflow\Concerns\HasStateHistory;
class Order extends Model implements HasStatesContract
{
use HasStates, HasStateHistory;
}
// Get all history
$order->stateHistory;
// Get history for specific field
$order->stateHistoryFor('state');
// Get previous state
$order->previousState();
// Get initial state
$order->initialState();
$history = $order->stateHistory->first();
$history->from_state; // 'pending'
$history->to_state; // 'processing'
$history->reason; // 'Order confirmed by warehouse'
$history->performer; // User model
$history->metadata; // ['key' => 'value']
$history->transitioned_at;
use Hpwebdeveloper\LaravelStateflow\Http\Resources\StateResource;
// Single state
return StateResource::make($order->state);
// All available states
return StateResource::collection($order->getAvailableStates());
// Response format
{
"name": "pending",
"title": "Pending",
"color": "yellow",
"icon": "clock",
"description": "Order is pending confirmation",
"is_current": true,
"can_transition_to": true,
"allowed_transitions": ["processing", "cancelled"]
}
public function show(Order $order)
{
return [
'order' => $order,
'current_state' => StateResource::make($order->state),
'available_states' => StateResource::collection(
$order->getAvailableStates()
),
];
}
// By state
Order::whereState('shipped')->get();
Order::whereNotState('pending')->get();
Order::whereStateIn(['pending', 'processing'])->get();
// By transition capability
Order::whereCanTransitionTo('shipped')->get();
// History-based
Order::whereWasEverInState('processing')->get();
Order::whereNeverInState('cancelled')->get();
use Hpwebdeveloper\LaravelStateflow\Validation\StateRule;
use Hpwebdeveloper\LaravelStateflow\Validation\TransitionRule;
// Validate state value
$request->validate([
'state' => ['
use Hpwebdeveloper\LaravelStateflow\Events\StateTransitioning;
use Hpwebdeveloper\LaravelStateflow\Events\StateTransitioned;
use Hpwebdeveloper\LaravelStateflow\Events\TransitionFailed;
// In EventServiceProvider
protected $listen = [
StateTransitioning::class => [
ValidateInventory::class,
],
StateTransitioned::class => [
SendOrderNotification::class,
UpdateInventory::class,
],
TransitionFailed::class => [
LogFailure::class,
],
];
// StateTransitioned event
$event->model; // The model
$event->field; // 'state'
$event->fromState; // 'pending'
$event->toState; // 'processing'
$event->performer; // User who performed transition
$event->reason; // Reason string
$event->metadata; // Additional data
use App\Services\NotificationService;
use App\Services\InventoryService;
class ShipOrder extends Transition
{
public function handle(
NotificationService $notifications,
InventoryService $inventory
): void {
$this->model->shipped_at = now();
$this->model->save();
// Services are automatically resolved from the container
$notifications->sendShippedNotification($this->model);
$inventory->decrementStock($this->model->items);
}
}