NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Open source

Lifecycle for Laravel

Install
composer require roundly-consulting/lifecycle-for-laravel
Requires: PHP ^8.4 · Laravel ^12.0|^13.0

Overview

Status lifecycles — a state machine — for any Eloquent model with a status: orders, tickets, listings, subscriptions, applications. Declare a model’s states and the named transitions between them once, in a small definition class — then every move goes through one guard pipeline that checks who may act, limits and race-free quotas, and writes an append-only history row. Expiry, “expiring soon” warnings, scheduled transitions and rollbacks are built in, and every refusal is a structured, translated reason your UI can show. Native and MIT-licensed, built only on Laravel and Roundly’s own foundation packages — no third-party dependencies.

What you get

Declarative definitions

Backed-enum or string states, an initial state, terminal states and named transitions with wildcards — several lifecycles per model.

One guard pipeline

Gate abilities, actor rules, reasons, payload validation, custom guards and deadlines — every refusal is a structured, translated Denial.

Limits and race-free quotas

Max occurrences, cooldowns, minimum dwell, rate limits and per-scope quotas that hold even under concurrent requests.

Expiry and scheduling

Per-state TTLs, grace periods, once-only warnings, extend and renew, and any transition scheduled for later — one sweep runs them all.

Rollbacks and history

Undo the last transition or roll back to a point, with windows, compensation and snapshots — on top of an append-only history.

Safe under concurrency

A row lock plus a compare-and-swap write per transition, after-commit events, optimistic versions and idempotency keys.

Facade, DI or actions

One Lifecycles facade, an injectable manager or single-purpose actions — plus a fake that refuses what the real engine refuses without a database.

Documentation

Installation

Install via Composer, choose key types, publish the migrations, and schedule the sweep when states expire or transitions are scheduled.

Configuration

Every config key, default and env variable — key types, subjects, strict writes, actors, transactions, history, rollbacks and schedules.

Defining a lifecycle

Declare states, the initial and terminal states and named transitions in a definition class — every builder method and how to validate it.

Models with a lifecycle

Implement LifecycleSubject, add the HasLifecycle trait and map attributes to definitions — several lifecycles per model, relations and reserved names.

The Lifecycles facade

Tour the Lifecycles facade — for($model) handles, model() helpers, the definitions() and schedules() accessors, the sweep and direct writes.

DI and actions

Skip the facade: inject LifecycleManager or call a single-purpose action with a request DTO — the handle → manager → action map.

Applying transitions

Apply named transitions with an actor, reason and payload — transitionTo(), attempt(), results, optimistic versions and idempotency keys.

Asking before acting

Check a transition before applying it — can(), check(), allowedTransitions() and the structured, translated Decision and Denial objects.

Restrictions and guards

The guard pipeline in order — system context, actor rules, Gate abilities, reasons, payloads, custom guards — and every denial code.

Limits, quotas and freezes

Max occurrences, cooldowns, minimum dwell, seals, rate limits, race-free quotas per scope, and freezing one lifecycle of a subject.

Handlers, hooks, stamps and snapshots

Run side effects inside the transaction with handlers and onEnter/onExit hooks, stamp timestamps, snapshot attributes and compensate on rollback.

Expiry

Per-state TTLs from an interval, a closure or a column — grace periods, once-only warnings, effectiveState() and extend, renew or neverExpire().

Scheduled transitions and the sweep

Schedule any transition for later and run due expiries, warnings and schedules with lifecycle:sweep — inline or queued, with retries.

Rollbacks and history

Undo the last transition or roll back to a history point — all or nothing, with windows, compensation and snapshots — plus the append-only history.

Querying

Query scopes for state, expiry, grace, freezes and time in state — plus withLifecycle() eager loading to avoid N+1 queries.

Strict writes and drift

Why direct status writes throw, which write paths bypass the engine, and how allowDirectWrites() and adoption reconcile drift.

Definitions and graphs

Inspect states and transitions with Lifecycles::model() and definitions(), and export Mermaid or DOT diagrams with lifecycle:graph.

API resources and validation

LifecycleResource and TransitionRecordResource for APIs, the ValidTransition and ValidState rules, and 422 or Retry-After from refusals.

Events

Every event, when it fires and its payload — listen for LifecycleTransitioned, not Eloquent’s updated, and keep side effects after commit.

Artisan commands

make:lifecycle, sweep, graph, validate, show, adopt and prune — signatures, options and when to run each.

Concurrency and databases

One transaction and lock order per change, compare-and-swap state writes, MySQL READ COMMITTED for quotas, deadlock retries and Octane.

Migrating from status enums

Map hand-rolled status enums, checks and commands onto the package, and move an existing table over with lifecycle:adopt.

Testing

Swap in Lifecycles::fake() to record transitions and refuse on demand, with every assertion — or test the real engine with factories and time travel.

Requirements

PHP 8.4+, Laravel 12 or 13, and SQLite, PostgreSQL or MySQL/MariaDB — with the package’s tables in the same database as your models.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.