Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Fsm Laravel Package

christhompsontldr/laravel-fsm

Robust finite state machine for Laravel with zero-config setup. Define states and transitions with guards, actions, and entry/exit callbacks. Event-driven with comprehensive transition logging, validation, caching, and support for multiple state machines per model column.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require christhompsontldr/laravel-fsm
    php artisan vendor:publish --provider="Fsm\FsmServiceProvider" --tag="fsm-config"
    
  2. Define States Create an enum implementing FsmStateEnum (e.g., app/Fsm/Enums/OrderStatus.php):

    enum OrderStatus implements FsmStateEnum {
        case Pending; case Paid; case Shipped; case Delivered;
    }
    
  3. Register FSM Definition Create a definition class (e.g., app/Fsm/Definitions/OrderStatusFsm.php):

    use Fsm\FsmBuilder;
    
    class OrderStatusFsm implements FsmDefinition {
        public function define() {
            FsmBuilder::for(Order::class, 'status')
                ->initialState(OrderStatus::Pending)
                ->state(OrderStatus::Pending)
                ->state(OrderStatus::Paid)
                ->from(OrderStatus::Pending)->to(OrderStatus::Paid)->event('pay')
                ->build();
        }
    }
    
  4. Add Trait to Model

    use Fsm\Traits\HasFsm;
    
    class Order extends Model {
        use HasFsm;
    }
    
  5. Trigger First Transition

    $order = Order::create(['status' => OrderStatus::Pending->value]);
    $order->fsm()->trigger('pay'); // Transitions to Paid
    

First Use Case

Order Workflow: Define a simple order status FSM with pending → paid transition. Use trigger() to process payments and validate state changes in real-time.


Implementation Patterns

Core Workflows

  1. State Transition

    // Direct transition (bypassing events)
    $order->transitionFsm('status', OrderStatus::Paid);
    
    // Event-driven transition (recommended)
    $order->fsm()->trigger('pay');
    
  2. Guarded Transitions

    FsmBuilder::for(Order::class, 'status')
        ->from(OrderStatus::Pending)->to(OrderStatus::Paid)
        ->event('pay')
        ->guard([PaymentValidator::class, 'validate'])
        ->build();
    
  3. Actions & Callbacks

    FsmBuilder::for(Order::class, 'status')
        ->state(OrderStatus::Paid)
            ->onEntry([SendReceipt::class, 'handle'])
        ->from(OrderStatus::Pending)->to(OrderStatus::Paid)
            ->action([LogPayment::class, 'log'])
        ->build();
    
  4. Multiple FSMs

    // Define separate FSMs for approval and publication
    FsmBuilder::for(Document::class, 'approval_status')->...
    FsmBuilder::for(Document::class, 'publication_status')->...
    
    // Trigger specific FSM
    $doc->fsm('approval_status')->trigger('approve');
    

Integration Tips

  • Event Listeners: Subscribe to StateTransitioned/TransitionFailed for audit trails:
    Event::listen(StateTransitioned::class, fn($event) => Log::info($event->model));
    
  • Queued Actions: Offload heavy tasks:
    ->queuedAction(NotifyCustomerJob::class)
    
  • Dry Runs: Validate transitions without side effects:
    $result = $order->fsm()->dryRun('cancel');
    

Common Patterns

Pattern Example
Terminal States ->state(OrderStatus::Delivered, fn($state) => $state->isTerminal(true))
Wildcard Transitions ->from(\Fsm\Constants::STATE_WILDCARD)->to(OrderStatus::Cancelled)
Metadata ->state(OrderStatus::Paid, fn($state) => $state->metadata(['color' => 'green']))

Gotchas and Tips

Pitfalls

  1. Caching Issues

    • Clear cache after definition changes:
      php artisan fsm:cache:clear
      
    • Tip: Use php artisan fsm:diagram to visualize FSMs and debug transitions.
  2. Transaction Conflicts

    • Disable use_transactions in config/fsm.php if callbacks fail due to deadlocks.
    • Tip: Wrap custom transactions around FSM operations for complex workflows.
  3. State Validation

    • Ensure initialState matches the database default to avoid null state errors.
    • Tip: Use can() to check transitions before triggering:
      if ($order->fsm()->can('pay')) { ... }
      
  4. Enum vs. String States

    • Prefer enums for type safety, but ensure database column matches the enum’s value.
    • Tip: Cast the column in your model:
      protected $casts = ['status' => OrderStatus::class];
      

Debugging

  • Log Failures: Enable log_failures in config/fsm.php to capture transition errors.
  • Event Context: Pass a DTO to trigger() for debugging:
    $order->fsm()->trigger('pay', new class implements ArgonautDTOContract {
        public function getUserId() { return auth()->id(); }
    });
    

Extension Points

  1. Custom Guards

    class PaymentGuard {
        public function __invoke($model, $from, $to, $context) {
            return $model->amount > 0;
        }
    }
    
  2. Dynamic States Override FsmBuilder to load states from a database or API.

  3. Event Modifiers Extend StateTransitioned to add custom logic:

    Event::listen(StateTransitioned::class, fn($event) => {
        if ($event->toState === OrderStatus::Paid) {
            cache()->forever("order_{$event->model->id}_paid", true);
        }
    });
    

Performance

  • Batch Transitions: Use fsm()->batch() for bulk operations (e.g., updating 1000 orders).
  • Cache Definitions: Enable event_logging with queue: false for high-throughput systems.

Anti-Patterns

  • Avoid Direct DB Updates: Always use transitionFsm() or trigger() to respect guards/actions.
  • Don’t Mix FSMs: Avoid overlapping state definitions for the same column.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope