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

State Machine Bundle Laravel Package

winzou/state-machine-bundle

Lightweight PHP/Symfony bundle for defining state machines on your domain objects. Configure graphs with states, transitions, and optional guard/before/after callbacks via YAML/XML/PHP, then apply and test transitions without hard-coded state logic.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require winzou/state-machine-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        winzou\Bundle\StateMachineBundle\winzouStateMachineBundle::class => ['all' => true],
    ];
    
  2. Define a Graph: Configure a state machine graph in config/packages/winzou_state_machine.yaml:

    winzou_state_machine:
        my_entity:
            class: App\Entity\MyEntity
            property_path: state
            graph: default
            states: [draft, published, archived]
            transitions:
                publish:
                    from: [draft]
                    to: published
                archive:
                    from: [published]
                    to: archived
    
  3. First Use Case: Inject the Factory service and apply a transition:

    use SM\Factory\Factory;
    
    public function publishAction(MyEntity $entity, Factory $factory)
    {
        $stateMachine = $factory->get($entity, 'default');
        $stateMachine->apply('publish');
        // $entity->state is now 'published'
    }
    

Implementation Patterns

Core Workflows

  1. State Validation: Check if a transition is allowed before applying it:

    if ($stateMachine->can('publish')) {
        $stateMachine->apply('publish');
    }
    
  2. Dynamic State Transitions: Use callbacks to enforce business logic:

    callbacks:
        guard:
            validate_publish:
                on: publish
                do: ['@my_service', 'validate']
                args: ['object']
    
  3. Multi-Graph Entities: Define multiple graphs for a single entity (e.g., workflow and audit):

    winzou_state_machine:
        my_entity:
            # ... default graph
        my_entity_audit:
            class: App\Entity\MyEntity
            graph: audit
            states: [active, suspended]
            transitions:
                suspend:
                    from: [active]
                    to: suspended
    
  4. Event-Driven Transitions: Trigger transitions from events (e.g., onPostPersist):

    public function postPersist(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        $stateMachine = $factory->get($entity, 'default');
        $stateMachine->apply('create');
    }
    

Integration Tips

  • Doctrine Events: Attach state transitions to Doctrine lifecycle events for automatic workflows.
  • Symfony Forms: Disable form fields based on state:
    $builder->add('title', TextType::class, [
        'disabled' => !$stateMachine->can('publish'),
    ]);
    
  • API Validation: Return 403 Forbidden if a transition is invalid:
    if (!$stateMachine->can('transition')) {
        throw new \Symfony\Component\HttpKernel\Exception\HttpException(403);
    }
    

Gotchas and Tips

Pitfalls

  1. Callback Argument Overrides: Avoid splitting args across multiple config files—only the last definition is used (fixed in v0.2.1).

  2. State Property Path: Ensure property_path in config matches the entity property (default: state). Use getState() to debug:

    $stateMachine->getState(); // Verify current state
    
  3. Circular Dependencies: Avoid callbacks that create infinite loops (e.g., a before callback applying another transition).

  4. Symfony Version Quirks:

    • Symfony 7+ requires explicit priority in callbacks for ordering:
      callbacks:
          before:
              log_transition:
                  on: publish
                  do: ['@logger', 'info']
                  args: ['object', 'Transitioning to published']
                  priority: 100
      

Debugging

  • Debug Command: Use the built-in debug command to inspect graphs:
    php bin/console debug:state-machine
    
  • Logging: Enable debug mode to log transitions:
    # config/services.yaml
    SM\Factory\Factory:
        calls:
            - [setDebug, [true]]
    

Extension Points

  1. Custom Guards: Extend the Guard class to add logic:

    use SM\Guard\GuardInterface;
    
    class MyGuard implements GuardInterface {
        public function check($object, $transition, $stateMachine) {
            // Custom validation
        }
    }
    

    Register in config:

    callbacks:
        guard:
            my_guard:
                on: '*'
                do: ['@my_guard_service', 'check']
    
  2. Dynamic Graphs: Load graphs dynamically via services:

    services:
        my_dynamic_graph:
            class: App\StateMachine\DynamicGraphBuilder
            tags: [winzou_state_machine.graph_builder]
    
  3. Event Listeners: Listen to state changes:

    use SM\Event\TransitionEvent;
    
    public function onTransition(TransitionEvent $event) {
        if ($event->getTransition() === 'publish') {
            // Handle publish event
        }
    }
    

    Register as a service with the kernel.event_listener tag.

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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui