abather/spatie-laravel-model-states-actions
Add state-based actions to Laravel views with this extension for spatie/laravel-model-states. Define per-state authorization abilities, skip checks when needed, and customize action icons and colors. Includes transition checks via canTransitionTo.
Architecture fit. The package remains a thin presentation-layer adapter between spatie/laravel-model-states (the state machine, source of truth) and Filament (the admin UI). v2.1.1 does not change that shape, but it widens the adapter's remit in two directions that are worth naming explicitly. First, notification support means the package now performs a side effect — dispatching a Laravel notification when a transition succeeds — not merely rendering buttons. The state machine is still authoritative, but the package is no longer purely additive-to-the-UI: it participates in the transition lifecycle. Second, live refresh introduces a background polling/refresh loop into the admin surface, which is a runtime behaviour concern rather than a rendering concern. Both are reasonable features for an admin panel, but they move the package from "declarative metadata" toward "opinionated transition orchestration." The original coupling smell is unchanged and slightly compounded: state classes now optionally carry notification and refresh configuration alongside heroicon-o-* strings and colour tokens, deepening the Filament/Laravel binding of what may be a shared domain asset. Still acceptable for an internal admin-only surface; still questionable if the state machine must serve an API, queue consumer, or non-Filament front end.
Integration feasibility. High and essentially unchanged mechanically — still a Composer require, a one-line extends change per state class, no schema migration. The new surface is opt-in, so an existing v2.0.x adoption is not disturbed. Expect two additions to the integration checklist: a publishable config (or per-state static properties) governing notification class, recipients, and channels; and a refresh/polling setting, likely at the table or action level. Both should be treated as configuration to review, not defaults to accept. The blast radius of a failed evaluation remains a single resource in a feature branch.
Technical risk.
@devmoath) is the first evidence of an outside contributor landing code, not just a name in the contributor list. That is a real positive signal for a four-star, zero-dependent package. It does not create a support contract, and the maintainer still has no stated support window, no surfaced CI status, and no docs site beyond the README. Treat it as "upstream is alive and accepting patches," not "upstream is durable."tableActions() and StateFilterService Gate checks, and it adds multi-user concurrency semantics (state changing under a user mid-action). Needs an interval config and a measured cost before it is enabled anywhere non-trivially sized.$skip_authorization = true and $requires_confirmation = false remain class-level switches that silently disable gating for every subclass when set on a base state class — including destructive transitions. Still a governance risk, still mitigated the same way.StateActionsService::make(static::getModel()) still derives available actions from StateConfig transitions, relying on Spatie internals outside a stability contract.composer audit on the resulting tree.Key questions.
view permission on the record receive state-change detail?Abather\...\State used anywhere that must remain framework-agnostic (API, CLI, jobs), or is it now more firmly UI-only because of the notification and refresh properties?$skip_authorization and $requires_confirmation are never set on an abstract base class in our codebase?Stack fit. Laravel + Filament + Spatie Model States remains a mainstream, well-documented trio, and the package still assumes exactly that combination. v2.1.1 strengthens the fit for teams whose transitions are user-driven from the admin UI — notifications and live refresh are exactly what an operations team asks for next ("tell someone when it moves," "don't make me hit refresh"). It simultaneously weakens the fit for system-driven transitions, where none of this applies. The presence of laravel-notification concerns also means the package now touches the notification subsystem, so stack fit should include "we already use Laravel notifications and have a queue worker" as a soft precondition for turning the feature on.
Migration path. For an existing v2.0.x adoption, near-zero:
2.1.1) in composer.json and commit the lockfile; do not float. Diff the published config against what we have before merging.App\States\State extends Abather\SpatieLaravelModelStatesActions\State; never extend the vendor class directly. This is now more valuable than before, because it is the single place to default notifications off and refresh off until each is deliberately enabled per resource.resources/lang/{locale}/states.php lint to cover any new notification copy so user-facing strings do not leak class names.Compatibility. Treat Filament majors as hard boundaries, as before. Add a boot smoke test that instantiates each action type including the new notification/refresh paths — namespace drift between Filament\Tables\Actions\Action and Filament\Actions\Action remains the most likely silent break, and the added surface increases the chance of catching it late. Verify the PHP floor against the runtime image and confirm a queue worker exists and is monitored before enabling queued notifications.
Sequencing.
Contingency. The fork path stays warm and is slightly more expensive now: an in-house replacement must reimplement notification dispatch and refresh, not just metadata. Still contained because every state extends App\States\State. Re-scope the fork estimate from half a day to roughly one to two days, and record that in the risk register so the number is not a surprise later.
Maintenance. Steady-state cost remains low; the spike profile is unchanged at Filament majors, plus a new low-amplitude cost at minors that add behaviour (configuration review, notification copy, refresh tuning). Budget 1–3 engineer-days per Filament major, and roughly half a day per behaviour-adding minor to review config and defaults. Because we own App\States\State, upstream gaps can be absorbed locally without forking the repository. Keep the package in the dependency register with a named owner and a review date, and note in that register that 2.1.x is the current line.
Support. Still no commercial support, no SLA, no vendor; GitHub issues only. The v2.1.1 release is a mild positive: an external contributor's PR was merged and shipped promptly, which suggests a maintainer who is present and responsive to patches right now. That is a snapshot, not a commitment — there is still no one to escalate to, and the fork remains the only escalation path. The conclusion is unchanged and now firmer: acceptable for a UI convenience layer, unacceptable for anything on the transaction-critical path. Notification support is precisely the kind of feature that tempts teams to promote this package onto that path; resist that until failure semantics are proven on a non-financial resource.
Scaling. The UI-side runtime cost is still modest, but live refresh changes the shape of the curve. Instead of per-row overhead on render, you now get per-row overhead on a timer, multiplied by open admin tabs and by user count. Benchmark a 200–500 row page with refresh enabled and three to five concurrent sessions before turning it on broadly; if material, raise the interval, restrict refresh to record views rather than list tables, or prefer actionsAsGroup() on the record view over per-row action columns. Notifications add queue throughput to the model — small, but it belongs in the queue-depth dashboard rather than being invisible.
Failure modes to monitor.
$skip_authorization = true on a shared base class. Code-review rule plus a static-analysis check.$requires_confirmation = false. Same mitigation.Ramp-up. Unchanged at roughly half a day for a developer fluent in Filament and Spatie: the mental model is "add static properties to your state classes." The new notification and refresh properties make the discovery-mechanism friction worse rather than better, because they are invisible at the call site and have real runtime consequences when set on a base class. The conventions doc is still the cheapest mitigation and should be written during the pilot, not after rollout. Extend it to one and a half pages: base class, authorization, localization, ordering, notification recipients and queueing, refresh interval policy, and the rule that these properties exist for UI and admin orchestration only — never as the sole enforcement point for a business rule.
How can I help you explore Laravel packages today?