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

Laravel Model Statable Laravel Package

orobogenius/laravel-model-statable

Add state and status management to any Eloquent model. Apply the trait to define allowed statuses, transition between them, query by state with scopes, and optionally track status history, all through a simple, expressive Laravel API.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the first public release (1.0.0), so there is no upgrade/migration path from an earlier version. Start with the README’s “preparing your model”, “defining states”, and “relationships” sections, then check the package tests/examples for the exact API shape.

Minimal adoption path:

  1. Install via Composer and publish any config/migration files the package provides.
  2. Add a state column to the target model, with a sensible database default.
  3. Add the package trait and state cast to the model.
  4. Define the base state class and concrete states/transitions.
  5. Apply one low-risk transition in an action or service class.

Typical shape to verify against the docs:

class Order extends Model
{
    use HasStates;

    protected $casts = [
        'state' => OrderState::class,
    ];
}

First practical use case: an order lifecycle such as Pending -> Paid -> Shipped -> Cancelled. Put the transition behind a service/action rather than directly in a controller, so validation, authorization, and side effects stay together.

Also use it for relationship filtering early: e.g. “customers with at least one paid order” or “only active subscriptions”, rather than loading all related models and filtering in PHP.

Implementation Patterns

Centralize the state machine. Define allowed transitions in the state classes or a dedicated transition map. Avoid scattering if ($model->state === ...) checks across controllers, jobs, and listeners.

Use action/service classes for transitions. A transition often triggers emails, invoices, inventory changes, or audit records. Keep that workflow in one place:

DB::transaction(function () use ($order) {
    $order->state->transitionTo(Paid::class);
    // side effects...
});

Verify whether your package persists the state automatically after transitionTo(); if not, call save() explicitly in the action.

Validate before applying. In form requests or actions, check whether the requested transition is allowed from the current state. Return a validation error or 409 Conflict rather than letting an invalid transition throw deep in the stack.

Drive UI actions from the state machine. Expose “available transitions” to the frontend so buttons/actions match what the backend will allow. This keeps admin panels and APIs consistent.

Filter relationships at the query level. Use the package’s state scope on relationship queries rather than filtering collections:

$paidOrders = Order::whereState('state', Paid::class)->get();

$customers = Customer::whereHas('orders', function ($query) {
    $query->whereState('state', Paid::class);
})->get();

For eager loading, constrain the relationship instead of loading everything:

$customers = Customer::with(['orders' => function ($query) {
    $query->whereState('state', Paid::class);
}])->get();

Use factories and tests for state coverage. Add factory states for common statuses and test:

  • valid transitions,
  • invalid transitions,
  • side effects inside transactions,
  • relationship queries by state,
  • authorization around who may transition.

Keep authorization separate. State transitions are business rules, not a replacement for policies. A user may be allowed to view an order but not transition it to Shipped.

Gotchas and Tips

Initial state and existing rows. Set both a database default and a model-level default if the package supports it. For existing tables, backfill the state column before making it non-nullable.

Confirm persistence behavior. Some state packages persist on transition; others only mutate the attribute. Check the README/tests and write a test that reloads the model from the database after a transition.

Race conditions matter. Two requests can try to transition the same model at once. Use a transaction plus row locking, or an atomic conditional update, when the transition has real consequences.

Do not mass-assign state from request input. Treat state as a guarded attribute or only change it through transition methods. Otherwise a user can bypass the state machine with a crafted payload.

Relationship filters are query constraints, not collection filters. whereHas() and constrained with() are the right tools. Filtering a loaded collection with ->filter() hides N+1 and pagination problems.

Index state columns used in relationship filters. If you frequently query “active” or “paid” related records, add indexes appropriate to your database and query patterns.

Watch serialization. A state cast may serialize as an object or class name depending on the package. Use API resources to expose a stable string/enum value to clients.

Be careful with raw database values. When debugging, check the actual stored value and confirm it maps to the expected state class. A typo in a state class or database value can look like a broken transition.

First-release caution. Pin the version in composer.json and review future changelogs closely. For a 1.0.0 package, validate the API against your Laravel version and test the specific workflow you depend on before rolling it out broadly.

Extension points to look for: custom state classes, transition hooks/listeners, query scopes, custom casts, and policy integration. These are the places to add audit logging, notifications, and authorization without forking the package.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console