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())
        );
    }
}

$order = Order::create(['customer_name' => 'John Doe']);

// Check current state
$order->state;                              // Pending instance
$order->state->name();                      // 'pending'
$order->isInState('pending');               // true

// Check allowed transitions
$order->canTransitionTo('processing');      // true
$order->canTransitionTo('shipped');         // false (must process first)
$order->getNextStates();                    // [Processing::class, Cancelled::class]

// Transition
$result = $order->transitionTo('processing');
$result->succeeded();                       // true
$order->state->name();                      // 'processing'

// 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
}

$result = $order->transitionTo(
    state: Shipped::class,
    reason: 'Shipped via FedEx',
    metadata: ['tracking_number' => 'FX123456789']
);

$result = $order->transition()
    ->to(Shipped::class)
    ->reason('Order shipped')
    ->metadata(['carrier' => 'FedEx'])
    ->execute();

$order->transitionToWithoutEvents('processing');

$order->forceTransitionTo('delivered');

// 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

// config/laravel-stateflow.php
return [
    // Default database column for state
    'default_state_field' => 'state',

    // Directory for generated state classes
    'states_directory' => 'States',

    // History tracking
    'history' => [
        'enabled' => true,
        'table' => 'state_histories',
        'prune_after_days' => null,
    ],

    // Permission system
    'permissions' => [
        'enabled' => true,
        'role_based' => true,
        'policy_based' => false,
        'throw_on_unauthorized' => true,
    ],

    // Event dispatching
    'events' => [
        'enabled' => true,
    ],
];

class Order extends Model implements HasStatesContract
{
    use HasStates;

    public static function registerStates(): void
    {
        static::addState('state', StateConfig::make(OrderState::class)
            ->default(Pending::class)
            ->allowTransition(Pending::class, Processing::class)
            ->allowTransition(Processing::class, Shipped::class)
            ->allowTransition(Shipped::class, Delivered::class)
            ->allowTransition(Pending::class, Cancelled::class)
            ->allowTransition(Processing::class, Cancelled::class)
        );
    }
}

public static function registerStates(): void
{
    // Order status (main workflow)
    static::addState('state', StateConfig::make(OrderState::class)
        ->default(Pending::class)
        ->allowTransition(Pending::class, Processing::class)
        ->allowTransition(Processing::class, Shipped::class)
        ->allowTransition(Shipped::class, Delivered::class)
        ->allowTransition(Pending::class, Cancelled::class)
        ->allowTransition(Processing::class, Cancelled::class)
    );

    // Payment status (separate workflow)
    static::addState('payment_status', StateConfig::make(PaymentStatus::class)
        ->default(Unpaid::class)
        ->allowTransition(Unpaid::class, Paid::class)
        ->allowTransition(Paid::class, Refunded::class)
    );
}

// Usage
$order->transitionTo(Processing::class, field: 'state');
$order->transitionTo(Paid::class, field: 'payment_status');

// app/Transitions/ShipOrder.php
use Hpwebdeveloper\LaravelStateflow\Transition;

class ShipOrder extends Transition
{
    public function handle(): void
    {
        $this->model->shipped_at = now();
        $this->model->save();

        // Send notification, update inventory, etc.
    }

    public function canTransition(): bool
    {
        return $this->model->shipping_address !== null
            && $this->model->total > 0;
    }
}

->allowTransition(Processing::class, Shipped::class, ShipOrder::class)

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);
    }
}
bash
php artisan vendor:publish --tag="laravel-stateflow-config"
bash
php artisan vendor:publish --tag="laravel-stateflow-migrations"
php artisan migrate

app/States/
  ├── OrderState.php      # Abstract base class
  ├── Pending.php
  ├── Processing.php
  ├── Shipped.php
  ├── Delivered.php
  └── Cancelled.php
bash
php artisan make:transition ShipOrder