masterix21/laravel-entitlements
A flexible Laravel package for managing feature entitlements, plans, and quotas. Define features, attach them to plans or Eloquent models, then check access, consume limited usage, and control what each user or team can do via a clean, expressive API. Ideal for SaaS feature gating.
Architecture fit
masterix21/laravel-entitlements is a domain package for plan/license entitlement management in Laravel: plans composed of typed items (slot, computed, etc.), licenses as the unit of allocation, usages as the consumption record, and PlanTransition as the state machine for upgrades/downgrades/cancellation. It fits products with seat-based or metered feature gating where entitlement state must be auditable rather than recomputed ad hoc.changePlan(), adds a database-level invariant, and makes domain events transaction-safe. These are exactly the defects that surface as intermittent, hard-to-reproduce entitlement drift in production.entitlements.models.* and a typed type_enum with label resolution make the package extensible without forking, which matters for long-term ownership.Integration feasibility
php artisan migrate, optionally publish translations and Filament assets. No service mesh, queue, or external dependency is introduced.entitlement_plan_items (plan_id, type). This is the single highest-friction item in the release: it will fail on any environment that already contains duplicate plan/item-type rows, which is plausible in databases that predate the Filament repeater validation.DB::afterCommit. Any listener that previously read uncommitted state (or that relied on being invoked before rollback) changes semantics. Listeners are now not invoked at all when the surrounding transaction rolls back — which is the correct behavior, but it is a silent behavioral break for existing listeners.lang/ to lang/vendor/laravel-entitlements/. Customized copies left in place will be silently ignored, producing untranslated UI rather than an error.forceRelease() now decrements slot_used by the usage amount instead of always 1. This corrects a real accounting bug but changes historical expectations; any reporting built on prior behavior should be re-baselined.filament-badgeable-column constraint tightened to ^4.0. This is a build-reproducibility fix; it will surface as a composer conflict only in projects that pinned the transitive dependency at a Filament 3-compatible version.Technical risk
changePlan() and slot operations is a deadlock surface that a patch release does not fully document.Key questions
filament-badgeable-column pin?entitlement_plan_items (plan_id, type)?Releasing state (the state the slot race fix protects)? Is remediation required, or does the code self-heal?afterCommit change?slot_used counters reconciled after the forceRelease() amount change — is there a recompute command, or is drift accepted?PlanTransition rows stuck in Pending or Failed?Stack fit
type_enum mean the package can host custom item types with registered resolvers without forking — the recommended extension path.Migration path
(plan_id, type) rows in entitlement_plan_items before publishing the new migration; remediate or merge duplicates. Treat this as a blocking prerequisite, not a post-deploy fix.vendor:publish --tag="entitlements-migrations" then php artisan migrate) in staging first, with a snapshot for rollback.lang/vendor/laravel-entitlements/ and delete the root copies to avoid shadowing confusion.PlanTransition events (and other entitlement domain events) for post-commit semantics. Update listeners that assumed pre-commit visibility; confirm that rollback-safe behavior is acceptable for each side effect (email, webhook, downstream provisioning).slot_used given the forceRelease() decrement change.filament-badgeable-column to ^4.0 and verify the Filament admin renders — the unregistered view namespace failure mode is a hard UI break, not a warning.Compatibility
afterCommit event dispatch (listener-dependent), and the translation path (asset-dependent). Version-number-based risk assessment is unsafe here; treat it as a minor release for planning purposes.Entitlements facade docblock now declares changePlan(), cancelTransition(), and applyDueTransitions(), which is documentation-only but improves static analysis and IDE support.TransitionAlreadyResolved surfaced as a notification instead of a 500) reduce support noise and are safe to adopt.Sequencing
Maintenance
Support
TransitionAlreadyResolved now produces a notification rather than a 500, and plan deletion for referenced plans is blocked with an explanation rather than failing at the database. Both were guaranteed support generators previously.IncompatiblePlanTransition being raised upfront for downgrades below current computed usage, and for plan changes toward empty plans, moves failures from post-commit inconsistency to a predictable, catchable exception. Support can now script a known response.Scaling
applyDueTransitions() path is batch-shaped and is the natural place to absorb deferred transition processing; the facade now documenting it suggests it is the intended escape hatch for load-shedding plan changes.Failure modes
(plan_id, type) rows — high likelihood if not pre-audited, high blast radius (deploy blocked). Mitigate with a pre-flight duplicate query.afterCommit change — low likelihood, high severity if a listener performed downstream provisioning that now has no compensating action.slot_used is now fixed, but existing data written under the old behavior may still be wrong. Treat historical counters as suspect and reconcile.Releasing still exist as a state; the fix prevents new instances from double-releasing but does not add a reconciler. Without an operational sweep, drift remains possible from prior occurrences.filament-badgeable-column version — now prevented by the ^4.0 pin, but any host app with an existing lockfile must run composer update on that package rather than assuming the constraint applies retroactively.Ramp-up
plan_transition config keys, all four PlanTransition events, a complete exceptions reference, plus the Boost guidelines — materially shortens this and is the most transferable part of the release.SlotStrategy and changePlan() directly before owning this package.How can I help you explore Laravel packages today?