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 Transition Machine Bundle Laravel Package

elcodi/state-transition-machine-bundle

Symfony bundle from the Elcodi e-commerce suite that provides state transition machine support for managing workflow states. This package is a read-only mirror maintained via the main elcodi/elcodi repository under the MIT license.

View on GitHub
Deep Wiki
Context7

Getting Started

Understand what you are installing before you install it. This repository is a read-only subtree split of elcodi/elcodi. It contains only the Symfony bundle wiring — the container extension, service definitions, and kernel registration. The actual state machine domain logic (StateMachine, State, Transition, guards) lives in the sibling elcodi/state-transition-machine component. Issues, PRs, and the real documentation live in the main elcodi/elcodi repository, not here. Anything you learn from this repo alone is incomplete by design.

  1. Install. Pin an explicit version — do not use dev-master in anything you intend to keep.

    composer require elcodi/state-transition-machine-bundle:^1.0
    composer show elcodi/state-transition-machine-bundle
    

    Immediately inspect composer.json for the PHP and Symfony constraints it drags in. This bundle targets Symfony 2/3-era conventions; if you are on a modern stack, install will either fail or force downgrades elsewhere in your tree. Find that out in a throwaway branch, not in main.

  2. Register the bundle (Symfony kernel — app/AppKernel.php on the era this targets):

    new Elcodi\Bundle\StateTransitionMachineBundle\ElcodiStateTransitionMachineBundle(),
    
  3. Read these in this order — the README is four paragraphs of community links and no usage docs, so treat the code as the spec:

    • DependencyInjection/ — the extension and configuration tree. This is the authoritative list of YAML keys and service IDs. Read Configuration.php before you write a single line of config.
    • Resources/config/ — service definitions. This shows you how machines, states, and transitions are actually constructed, and which IDs you can inject.
    • The elcodi/state-transition-machine component's source and its test suite — that is where the transition semantics (guards, callbacks, exception behavior) are defined and demonstrated.
    • elcodi.io/docs — the only prose documentation that exists.
  4. First use case — order lifecycle. The canonical one: cart → pending → paid → shipped → delivered, with → cancelled branches. Configure one machine, one state, and one transition, then exercise it in a test before it touches checkout. Confirm the exact transition API from the component source rather than guessing from bundle config.

  5. If you are on Laravel, stop at step 1. There is no composer require path here that ends well. The realistic "getting started" for a Laravel team is a one-hour spike: composer require in a scratch branch, open vendor/elcodi/, read the component's transition logic, and decide whether to reimplement it. The day-to-day leverage you get from this package in a Laravel codebase is the pattern, not the dependency.

Implementation Patterns

Configuration-first machine definition. States and transitions belong in YAML under the bundle's config key, not in controllers or model methods. One file per workflow (order_states.yml, payment_states.yml, shipment_states.yml), imported from the main config. Keep machine and state identifiers stable — they are frequently interpolated into service IDs, so renaming a state in config can silently change what you can inject.

Inject the machine; never construct it. The bundle registers machines as container services. Reference the specific machine ID in the service that needs it and let the container hand you the configured instance. The payoff: transition rules live in exactly one place, and other bundles (payment, shipping) can contribute to or observe the same machine instead of re-deriving your order lifecycle.

Guard the transition, not the caller. Encode preconditions in the transition definition (guards) or in a listener that vetoes the transition, not in the calling controller. If your code says if ($order->isPaid()) { ship(); }, the state machine has been reduced to decoration. The value of the pattern is that an illegal transition fails loudly regardless of which code path attempts it.

Use listeners for side effects and audit. Attach listeners to transition events to write audit rows, dispatch notifications, or reserve stock. This is the main extension point the bundle gives you. Keep listeners idempotent — a retried transition must not double-charge or double-ship.

Persist state outside the machine. The machine decides legality; your entity owns the current state. Apply the new state and the side effects in one transaction so you cannot end up with a paid order that was never marked paid.

Keep transition rules readable by product. The strongest argument for a config- or table-driven definition is that a non-engineer can review "can a refunded order be re-shipped?" as data rather than as control flow. If the definition lives in a format only one engineer understands, you have lost the main benefit.

Laravel pattern — do this instead of adopting the bundle. Write ~150 lines: a HasStateMachine trait on the model, a transitions() definition (array or config file), a transitionTo(string $state) method that validates legality, wraps in a DB transaction, records an audit row, and fires a domain event. Keep the definition declarative and tested:

class Order extends Model {
    use HasStateMachine;
    public static function transitions(): array {
        return [
            'pending'  => ['paid', 'cancelled'],
            'paid'     => ['shipped', 'refunded'],
            'shipped'  => ['delivered', 'refunded'],
            'delivered'=> ['refunded'],
            'refunded' => [], 'cancelled' => [],
        ];
    }
}
$order->transitionTo('paid'); // throws IllegalTransition; logs; fires OrderPaid

This gives you the same guarantees the bundle was aiming at, with no Symfony container, no legacy dependency floor, and code your team can debug on day one.

Gotchas and Tips

  • Read-only means read-only. The README explicitly states no PRs are accepted here. Do not patch vendor/ and do not fork casually — the split is regenerated from the parent, so local changes are fragile and the canonical issue tracker is elsewhere. A fork is an ownership commitment, and owning a state machine is a bigger promise than it looks.
  • Zero stars, zero dependents, no support contract. For anything touching orders or payments, the absence of a security-advisory process is disqualifying on its own. There is no release cadence to plan upgrades against.
  • The README is not documentation. It is community links. Your debugger and the DI extension are the documentation. Budget for reading Configuration.php line by line the first time.
  • Version constraints are the first landmine. Legacy Symfony/PSR constraints will fight a modern framework. Run composer why-not before you spend time on a real integration; the answer is usually "this package predates three major Symfony releases."
  • Symfony/Laravel mismatch is structural, not cosmetic. You would be bridging the service container, the event dispatcher, and the bundle's configuration assumptions. Laravel's package discovery does not map onto Symfony bundle conventions, and you gain nothing in return.
  • Don't patch the subtree split. If you genuinely must change behavior in an Elcodi-based app, change it in the parent elcodi/elcodi checkout or in your own adapter layer. Editing the split risks losing the change on the next subtree update, and creates a fork nobody is tracking.
  • Debugging: bin/console debug:container <machine-id> (or container:debug on older Symfony) to confirm the machine exists and which definition won. Most "transition not allowed" confusion is a config key that never loaded because the file was not imported.
  • Illegal transitions should throw, not return false. If you find yourself checking a boolean return everywhere, you have reintroduced the scattered conditionals the machine was meant to remove.
  • Transaction boundaries and idempotency: listeners that are not idempotent will bite you on retries, and state changes outside the transaction will drift from side effects. This is true of the bundle and of any replacement you write.
  • Extension points you can rely on: transition listeners and the container's machine services. Everything else is internal wiring you should avoid depending on.
  • The takeaway is the pattern, not the package. Explicit, enforced, auditable state transitions for orders and payments are worth designing for. This abandoned Symfony bundle is not the way to get them in a Laravel application — a maintained workflow library or a small in-house implementation will serve you better today and every day after.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log