Weave 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

samuel4x4/state-machine-bundle

samuel4x4/state-machine-bundle provides a simple state machine for PHP/Laravel apps: define states and transitions, guard changes, and hook actions into workflow updates. Keep status logic centralized, explicit, and testable instead of scattering conditionals across models.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Confirm what you're adopting first. Despite the name, this is packaged as a Symfony bundle (-bundle suffix, typical DependencyInjection/*Extension.php + *Bundle.php layout). Laravel does not have a bundle concept, so if you're a Laravel developer you should check composer.json for "type": "symfony-bundle" before assuming it drops in. If it does, plan to use the underlying library/service classes directly rather than the bundle wiring.
  • Read in this order: README.md → composer.json (type, require, autoload paths) → the bundle class and any Resources/config/services.* files → the core state-machine classes under src/. The bundle's DI wiring is the only Symfony-specific part; the state machine logic itself is usually framework-agnostic.
  • Minimal install (if library-only usage):
    composer require samuel4x4/state-machine-bundle
    
    If it registers Laravel auto-discovery (check extra.laravel in composer.json) you'll get a service provider; otherwise you'll need to use classes directly or write a thin provider.
  • First use case: model an Order status flow (pending → paid → shipped → delivered, with cancel side-transitions). Start by defining the transition map, then wire one guard (e.g. canShip requires paid and non-empty shipping_address). Get that working before touching persistence.
  • Where to look when stuck: the repo has 0 stars and 0 dependents, so expect little community material. Your primary references are the source, existing tests under tests/, and the bundle's example/config fixtures.

Implementation Patterns

  • Treat it as a workflow engine, not an ORM feature. The durable pattern: keep your Order model in Eloquent and store status as a string/enum column; use the package only to evaluate and apply transitions. This keeps you portable if you later swap in symfony/workflow or spatie/laravel-model-states.
  • Centralize transition definitions. Define one OrderStateMachine (or config array) and inject it wherever status changes happen — controllers, jobs, listeners. Avoid re-declaring transitions in multiple places; drift is the #1 source of bugs in state-machine code.
  • Wrap the transition, not the model. Do:
    $machine->apply($order, 'ship'); // throws if guard fails
    $order->save();
    
    rather than mutating $order->status directly. The value is the guard + transition validation, so use it on every write path.
  • Guard against race conditions. If two webhooks can fire concurrently, wrap the transition + save in a DB transaction and re-read the row (or use lockForUpdate()). State machines validate state at check time; without a transaction you can still double-fire paid → shipped.
  • Event/logging integration. Transition points are the natural place to emit Laravel events (OrderShipped, OrderCancelled) inside your wrapper. This is usually the extension point, not the bundle's internals — dispatch from the service you wrote around the machine.
  • Laravel integration pattern (when the bundle wiring doesn't apply):
    • Register the machine classes as singletons in an AppServiceProvider (or a small custom provider).
    • If the bundle exposes a config schema, mirror it into config/state_machine.php and mergeConfigFrom() in your provider.
    • Bind the machine's transition config in AppServiceProvider::register() so it's resolvable from app().
  • Testing pattern: write a PHPUnit/Pest test that asserts the transition matrix — each state × event either succeeds or throws with the expected exception. That's your regression net for a package with no public dependents.

Gotchas and Tips

  • Symfony vs Laravel wiring is the biggest trap. Do not assume composer require "just works." Verify:
    • extra.laravel — if absent, no auto-discovery.
    • Whether the bundle needs Symfony's EventDispatcher, Cache, or Config components — if it type-hints Symfony\Contracts\EventDispatcher\EventDispatcherInterface you'll need Laravel's bridge or a shim.
    • Whether it expects config/bundles.php registration (Symfony-only).
  • Zero-dependents means zero external footguns and zero battle-testing. Pin the version (composer require samuel4x4/state-machine-bundle:^X.Y). Read the CHANGELOG/releases before upgrading; a future-dated release (2026-02-16) is odd, so confirm the tag is stable and not a pre-release.
  • Exceptions over booleans. A good state-machine package throws on invalid transitions; if this one returns false silently, wrap it and throw yourself — otherwise invalid transitions become silent data corruption.
  • Config cache quirks. If the bundle ships a Symfony extension, bin/console config:dump won't exist in Laravel and config:cache won't know about it. Any bundle default config must be flattened into your Laravel config and merged manually.
  • Guards need side-effect discipline. If a guard checks external services (payment gateway, inventory), it can fail transiently — don't let a guard throw a generic exception; wrap in a domain exception so the caller can distinguish "invalid transition" from "guard couldn't verify."
  • Debugging invalid transitions: log the current state, attempted event, and the transition map at the failure site. Without this you get a useless TransitionNotFoundException with no context.
  • Extension points to favor: guards/callbacks and transition listeners are usually the only stable extension surface. Treat internal src/ classes as unstable — wrapping them in your own service class lets you swap implementations later (especially since this package has no dependents to pressure-test its API).
  • When to skip it entirely: if you only need simple enum-based status checks, spatie/laravel-model-states or a hand-rolled Transition class will be lower risk. Reach for this package when you need named events, guards, and a reusable transition definition — and you're willing to own the Symfony-vs-Laravel glue.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle