spatie/laravel-model-states
Add state and state machine behavior to Laravel Eloquent models. Represent each state as a class, automatically serialize to/from the database, and perform clean, explicit transitions with configurable rules—ideal for workflows like payments, orders, and approvals.
Domain-Driven Design (DDD) Alignment: Enables clean separation of state logic from model logic, improving code organization and maintainability for complex workflows (e.g., order processing, subscriptions, or approval pipelines).
State Machine Patterns: Facilitates implementing finite state machines (FSMs) for workflows with clear transitions (e.g., Draft → Published → Archived for content).
Build vs. Buy: Avoids reinventing state management wheels; leverages a battle-tested, MIT-licensed package with 1.3K+ stars and active maintenance.
Use Cases:
Pending → Shipped → Delivered), payments (Pending → Paid → Refunded), or user accounts (Active → Suspended → Banned).Paid vs. Failed states).Paid → Pending reversals) or enforce business rules (e.g., Failed payments require manual review).StateChanged) for compliance or debugging.Paid → Pagado) via the $name property.Roadmap Enablers:
Beta → Live transitions for gradual rollouts).Failed payments?").Look Elsewhere If:
status column (e.g., active/inactive). Use Laravel’s built-in casts or accessors instead.UserRole with no workflow). A plain enum or foreignKey relationship suffices.state column with raw strings.Shipped if Inventory is OutOfStock"). Consider a workflow engine like Camunda or custom services.Adopt If:
Paid has color(), Failed has notifyAdmin()).Draft → Published").OrderState reused for Invoice and Shipment)."This package lets us model complex workflows—like order processing or payment statuses—as first-class citizens in our codebase. Instead of hacking together status columns or spaghetti logic, we’ll define clear states (e.g., Pending, Paid, Failed) with rules for how they transition. This reduces bugs, makes business logic explicit, and gives us flexibility to change workflows without rewriting core systems. For example, if we need to add a Refunded state to payments, we just create a new class—no database migrations or model refactors. It’s like upgrading from spreadsheets to a proper workflow engine, but with the simplicity of Laravel."
ROI:
Paid → Pending reversals).*"This is a type-safe, event-driven state machine for Eloquent models, built on Laravel’s ecosystem. It solves the problem of managing complex workflows (e.g., orders, payments, user accounts) by:
Paid, Failed) is a class with its own methods (e.g., color(), notify()).Pending → Paid) at the model level, with optional validation.state = 'paid') but work as objects in code.Why not roll our own?
Example Use Case:
For a subscription system, replace a status column with:
// Model
class Subscription extends Model {
use HasStates;
protected $casts = ['state' => SubscriptionState::class];
}
// States
class Active extends SubscriptionState {
public function canCancel(): bool { return true; }
}
class Cancelled extends SubscriptionState {
public function canCancel(): bool { return false; }
}
// Transitions
SubscriptionState::config()
->allowTransition(Active::class, Cancelled::class)
->denyTransition(Cancelled::class, Active::class);
Now, subscription->state->canCancel() is type-safe and transition rules are enforced."*
Trade-offs:
state column per model (but this is explicit and intentional).How can I help you explore Laravel packages today?