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

Spatie Laravel Model States Actions Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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.

  • Version cliff at v2.0.0 is unchanged; the line has moved to v2.1.x. The v2 major raised the PHP floor and added Filament v4 support, stranding Filament v3 consumers on v1.0.x with no stated support window. v2.1.0/v2.1.1 are minor/patch releases with no breaking-change signal, so the upgrade from v2.0.x should be low-cost — but pin to an exact minor and verify the 2.1.x tag against the 2.0.x line before assuming drop-in.
  • Bus factor improved, marginally but genuinely. A merged external pull request (#5, @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."
  • Notification side effects sit near the transaction boundary. The highest-value question about v2.1.1 is whether notification dispatch happens before or after commit, and whether it is queued. If a transition commits and the notification throws, the user sees an error on a successful state change and may retry — a partial-success failure mode that is materially worse than a broken button. This is the single most important thing to verify in the spike.
  • Live refresh is a load multiplier, not a load constant. Polling turns one page render into N renders per open tab over time. It directly amplifies the pre-existing per-row concern around 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.
  • Authorization foot-guns unchanged. $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.
  • Reflective coupling unchanged. StateActionsService::make(static::getModel()) still derives available actions from StateConfig transitions, relying on Spatie internals outside a stability contract.
  • Documentation lag on the new features. The release note is a single line. There is no stated contract for notification payload shape, recipient resolution, queueing, or refresh interval defaults. Budget time to read the source rather than the changelog — and treat the absence of a migration note as a reason to diff config between 2.0.x and 2.1.1.
  • Metadata quality caveat carries forward: repository description typos, an anomalous last-release date, no stated test matrix. Re-verify attribution on Packagist and run composer audit on the resulting tree.
  • Mitigating factor unchanged. The surface is small and enumerated; a like-for-like in-house implementation is plausibly 150–300 lines. The real risk remains "will we notice we need to," and the new features make an unnoticed drift slightly more likely because they add behaviour behind static properties.

Key questions.

  1. Does v2.1.1 drop in cleanly on our current Filament major, and is there now a committed v4 upgrade path within our planning horizon?
  2. Are notifications dispatched inside or outside the database transaction, and are they queued by default? What does the user see if the notification fails after commit?
  3. How are notification recipients resolved — static config, model attributes, or role/Gate lookups — and can a recipient who lacks view permission on the record receive state-change detail?
  4. What is the live-refresh default interval, is it opt-in or opt-out, and what does it do to query count on a 200–500 row page with several open tabs?
  5. Is 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?
  6. What is the actual test coverage, and does CI run on the PHP/Laravel/Filament matrix we support — including the new notification and refresh paths?
  7. Has anyone audited that $skip_authorization and $requires_confirmation are never set on an abstract base class in our codebase?
  8. What is the owner and trigger threshold for the fork decision (e.g., no release for 12 months, or a broken Filament major)?

Integration Approach

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:

  1. Pin to an exact minor (2.1.1) in composer.json and commit the lockfile; do not float. Diff the published config against what we have before merging.
  2. Keep the project-owned base class 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.
  3. No data migration. The persisted state column is unchanged.
  4. Extend the existing resources/lang/{locale}/states.php lint to cover any new notification copy so user-facing strings do not leak class names.
  5. Enable notification support on one transition in one resource first, with an explicit recipient rule, then confirm the transition still commits and the UI behaves correctly when the notification channel is deliberately broken.

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.

  1. Week 1 — spike branch, unchanged scope: pilot on one internal, non-financial resource (content or ticket model, 3–4 states). Validate icons, colours, localization, filter, and policy gating end to end. Do not enable notifications or live refresh in this step.
  2. Week 1 — upgrade the pilot to 2.1.1 with both new features off, proving the patch is non-disruptive. This is the cheap test of the "no breaking change" claim.
  3. Week 2 — enable notification support on one transition only. Verify transaction boundary and failure behaviour (channel down, queue down, invalid recipient).
  4. Week 2 — benchmark live refresh at a realistic interval on a 200–500 row page with multiple concurrent admin sessions. Decide interval or defer.
  5. Week 2 — write/extend the one-page conventions doc: base-class rule, authorization rule, localization rule, ordering rule, and now a notifications rule (who receives, queued or not, on-failure behaviour) and a refresh rule (interval, which resources).
  6. Week 3+ — roll out to remaining resources, lowest-risk first. Hold payment/order/financial state machines until after the second resource is stable and notification failure semantics are proven.
  7. Standing — track upstream releases; re-evaluate at each Filament major, at any 12-month upstream silence, and at each minor that adds behavioural surface (as 2.1.0/2.1.1 did).

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.

Operational Impact

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.

  • Silent authorization bypass — $skip_authorization = true on a shared base class. Code-review rule plus a static-analysis check.
  • Missing confirmation on destructive transitions — $requires_confirmation = false. Same mitigation.
  • Partial success on notification failure — transition commits, notification throws, user sees an error and retries. Test with the channel and the queue deliberately broken; confirm the UI reports the transition as successful and the notification as a separate concern.
  • Duplicate or misdirected notifications — retries, queue redelivery, or over-broad recipient rules sending state-change detail to users who cannot view the record. Verify recipient resolution against policy.
  • Notification storms from bulk actions — a mass transition notifying hundreds of recipients. Check for batching or suppression before enabling on any high-volume resource.
  • Refresh-induced load — background polling raising query volume and DB connection pressure during peak admin hours. Watch p95 query time and connection saturation after enabling.
  • Stale-state races — live refresh updating the UI while a user has an action dialog open, leading to an attempt against a stale expected state. Confirm the result is a validation message, not a 500.
  • Transition exceptions surfacing as 500s — Spatie throws on invalid transitions; unchanged, still test explicitly.
  • Untranslated states and notification copy — missing keys render class names or raw strings to end users. Enforce with a test.
  • Filament upgrade breakage — class-not-found or signature drift on action namespaces. Catch at boot with a smoke test covering the new paths.
  • Upstream abandonment — now less silent than before, since releases are visible; still pre-commit to the fork decision and its trigger.

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.

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.
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder
ideatica/config-writer
keaton-php/pipe-dream
jacksonwebservices/laravel-user-suite
maksimru/conditional-laravel-debugbar
gismatul/nova-support-chat
gismatul/laravel-support-chat
networkrailbusinesssystems/oracle-api
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
refinery29/php-cs-fixer-config
parsnick/steak