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

Filament Change Requests Laravel Package

amjadiqbal/filament-change-requests

A Filament plugin for managing change requests: users submit proposed edits to records, reviewers approve or reject them, and every change is tracked—giving teams a simple, controlled review workflow for sensitive data.

View on GitHub
Deep Wiki
Context7

Getting Started

This package bolts an approval workflow onto existing Filament resources: instead of an edit saving straight to the record, the change is held as a change request, reviewed by someone else, then applied. It ships both halves — the submission side (page traits) and the review side (a resource, page, column, filter, action and widget).

Minimal path to a first working loop:

  1. Check your stack first. v1.0.0 requires PHP 8.3+, Laravel 12.4+ or 13, and Filament 4 or 5. If you're on Laravel 11 or Filament 3, this release is not installable — upgrade the app before evaluating the package.
  2. composer require amjadiqbal/filament-change-requests, then follow the README for migrations and asset/config publishing.
  3. Register the plugin on the panel(s) that should expose the review UI, and run migrations — the change requests table is the backbone of everything else.
  4. Pick one resource as a pilot. Add SubmitsChangeRequests to its EditRecord page class. Do not roll it out panel-wide on day one.
  5. Submit an edit as a normal user, then log in as a different user and open the Change requests resource (pending-only by default). Approve from the row action, then check the record.

Where to look first, in order:

  • The README installation section — plugin registration and config publishing are the two steps people skip.
  • The config file — this is where sensitive-attribute masking and review authorization hooks will live. Read it before writing any code.
  • The ChangeRequest model + migration — you will end up querying this table directly for reporting, and you'll want to know its column names.
  • The submission traits (SubmitsChangeRequests, SubmitsCreationRequests) — small, and they define the exact UX contract you're signing up for.

First meaningful use case: a content or catalogue model where a junior editor drafts changes and a senior approves them. That exercises held edits, the diff page, the badge, and the authorization chain all at once.

Implementation Patterns

Submission side — hold edits, then hold creates. The two traits are separate on purpose. SubmitsChangeRequests on EditRecord intercepts saves; SubmitsCreationRequests on CreateRecord holds the new record itself. In both cases the success notification becomes "submitted for approval" rather than "saved"/"created" — that copy change is the whole point, because a user who thinks a record saved will be confused when it hasn't. Apply them per page class, so you can enable approval on EditRecord while leaving a lightweight CreateRecord untouched if creation is low-risk.

Review side — treat the resource as a queue, not a CRUD screen. Default Pending filtering plus status/record-type/record filters means the resource is designed for triage. The practical workflow:

  • Reviewers live in the pending view with the nav badge as their inbox count.
  • Record-type filter to split queues by team (e.g. product team vs. content team).
  • Record filter to jump to "everything open against this one record" during an incident.
  • Bulk approve for the boring 90% (bulk publish, category tweaks); single-row approve for anything touching money, pricing, or permissions.

Diff page is the decision surface. Field-by-field diff with sensitive attributes masked is where reviewers actually spend time. Configure the mask list deliberately rather than inheriting defaults — anything that looks like a secret, token, PII field, or internal note should be masked. Rollback on the same page means a bad approval is recoverable without a DB console, which is the single biggest reason to prefer this over a hand-rolled status column.

Bring review into the record context. ViewChangeRequestsAction on the record page and PendingChangesColumn on the table both remove the "open another resource and search" step. Pattern: put the action in the record page header for editors ("what's outstanding on this thing?"), and the column on index tables so a manager scanning a list can see which rows have work in flight. PendingChangesFilter pairs with the column — a "has pending changes" filter is what you want bookmarked.

Dashboard widget for reviewers, not for everyone. PendingChangeRequestsWidget is only useful to people who will act on it. Gate its visibility by the same check that governs review rights, otherwise every user gets a permanent "0 pending" panel that trains them to ignore widgets.

Authorization is a four-layer chain — design it intentionally. Order is: self-review blocked by default, then authorizeUsing(), then the reviewChangeRequest gate, then the model's policy. Use each layer for what it's good at:

  • Keep the self-review block on. It's the control that makes the whole feature meaningful; lifting it should be a conscious, documented decision for a specific model.
  • authorizeUsing() for programmatic, request-specific rules — e.g. a reviewer may only approve changes to records in their own tenant or region.
  • The reviewChangeRequest gate for a global answer ("is this person a reviewer at all?"), registered once in a service provider.
  • The model policy for per-model nuance ("editors can review blog posts but not invoices").

Resist putting everything in one layer. Debugging a 403 is far easier when you know which layer is supposed to be answering.

Held creations link forward. Approving a creation links the change request to the record it produced. That means a pending create leaves no orphan record, and after approval you can navigate from the change request to the real row. Rely on this for auditability — "who created this and who approved it" is one join, not a forensic exercise.

Gotchas and Tips

Tests that submit and approve as the same user will fail. Self-review is blocked by default. Every feature test needs two actors (actingAs($editor) … actingAs($reviewer)). This bites immediately in existing test suites that assumed a single authenticated user could do everything.

Order of the authorization chain is a debugging trap. Because the gate sits above the model policy, a permissive global gate quietly overrides a restrictive policy. If a reviewer can approve something your policy says they shouldn't, check the gate before touching the policy.

Approving a masked field means approving blind. Masking protects secrets, but it also removes information from the reviewer's decision. For high-stakes fields, prefer blocking the edit from being held at all, or require a second, differently-authorized approval, rather than letting someone approve a value they can't read.

Version constraints are tight. Laravel 12.4+ / 13 and Filament 4–5 only. Filament 5 support is brand new in this release — if you're on Filament 5, smoke-test the resource, page, column, filter, action and widget individually, since those are five separate integration points and new major-version support is where the rough edges concentrate.

Verify rollback semantics before you rely on it. "Roll back" on the request page could mean revert-the-record-to-previous-values, or re-open-the-request. Read the README and test both on a disposable record. If it reverts the record, confirm whether it creates a new change request (which then needs approving) or applies immediately — that ordering matters for your audit trail.

Bulk approve is a batch of independent approvals. Expect partial success on large selections and handle it: check whether the UI reports per-row failures, and keep bulk selections modest (tens, not thousands) until you've seen how it behaves on your data volume.

Pending-by-default filter hides history. Reviewers will ask "where did the approved ones go?" Teach the filter, or set a default the first week and document it. Same for the record-type filter when someone claims a change has "disappeared."

Watch the query cost of the badge and column. A nav badge that counts pending on every panel render, plus a PendingChangesColumn on a large index table, is a real cost. Ensure the change requests table is indexed on status, record type and record id (morph columns especially). If the badge feels slow, that's the first thing to check.

The notification copy is a contract. If your own code fires its own success notification, or your edit page redirects somewhere that implies a save, you'll contradict the trait's "submitted for approval" message. Audit getRedirectUrl() and any custom Notification::make() in the pages you add the traits to.

Extension points worth knowing early: the config file (masking, defaults), the authorizeUsing() hook, the reviewChangeRequest gate registration, and the widget/column/filter/action classes — all of which can be swapped or subclassed if the built-in review resource doesn't match your panel's IA. If you need a very different review screen, register your own resource against the same model rather than fighting the shipped one.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin