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 Audit Toolkit Laravel Package

digit7s/filament-audit-toolkit

A Filament audit toolkit for Laravel apps that captures model and user activity, displays changes and history in a clean panel UI, and helps teams quickly review who changed what, when, and from where. Includes configurable audit trails, filters, and a searchable timeline for fast investigation.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a patch release (v0.1.1) on a pre-1.0 package, so treat the API as stabilizing rather than stable. There are no breaking changes, but there are behavior changes in query construction that are worth verifying before you assume your existing code is unaffected.

Start here:

  1. Install and publish — composer require <vendor>/<package>, then publish config and migrations (php artisan vendor:publish --tag=<package>-config and -migrations) and run php artisan migrate. Confirm the audit table exists and is indexed on occurred_at (this package orders by it heavily; an unindexed occurred_at is the first thing that will bite you at volume).
  2. Look at the Audit Explorer first. It is the primary read surface and the thing this release changed. Open it with seeded data before wiring up any writes — it will tell you immediately whether your occurred_at values, event names, and categories are shaped as the package expects.
  3. Then look at the config file, specifically anything governing event/category sources, default ordering, and authorization hooks. The release notes call out "authorization boundaries" as a tested concern, which implies the Explorer's filters are expected to be gated — find where that gate is registered before you expose the Explorer to non-admin users.
  4. First use case: record one audit entry for a known model event (create/update/delete on a test model), then in the Explorer filter by that event, then by its category, and confirm you get exactly one row back and that it appears in the expected chronological position. That single loop exercises recording, filtering, distinct-ness, and ordering — the exact surface area of this release.

If your app is MySQL-only and you had not yet exercised the Event or Category filters, this is the release that makes those filters correct. Do that smoke test before anything else.

Implementation Patterns

Filter + ordering is the fragile combination. The two fixed bugs share a root cause: a DISTINCT filter query inherited an occurred_at ordering it was not designed to carry. In MySQL, SELECT DISTINCT ... ORDER BY occurred_at is only legal when occurred_at is in the select list, and even when legal it can change which row survives deduplication. Pattern to follow: when you build custom filter queries against the audit table, decide explicitly whether you are in "distinct set of values" mode or "chronological records" mode, and do not mix them. Distinct-value queries should not inherit the Explorer's default ordering.

Audit Explorer ordering. The Explorer is now guaranteed chronological. If you paginate it, add a deterministic tiebreaker (occurred_at, then id) — same-second events are common in tests and in bulk operations, and without a tiebreak your pagination will show duplicates and drop rows across page boundaries even though the base ordering is correct.

Authorization at the query builder, not the controller. The regression suite covers authorization boundaries, which suggests the intended pattern is that the gate is applied to the underlying query. Apply your policy as a scope before the Explorer's filters run ($query->visibleTo($user) then the package's filter methods), not as a post-hoc ->filter() on a collection — filtering after hydration will leak row counts through pagination totals.

Cross-database integration. The package now ships regression coverage for both SQLite and MySQL. Mirror that in your own app tests: run the Explorer feature tests on SQLite for speed in CI, but keep at least one MySQL-backed test for anything involving DISTINCT, GROUP BY, or ordering, because that is precisely the class of bug SQLite will not catch for you.

Event and Category as first-class filters. With these fixed, the natural pattern is to drive Explorer UI dropdowns from the same distinct-value queries the package exposes rather than hand-rolling pluck() calls against the audit table. You get the empty-table behavior fixes for free and stay consistent with the package's notion of an event name.

Gotchas and Tips

  • You may have workarounds to delete. If you previously patched around the duplicate-value or ordering bugs with a custom scope, an orderByRaw, or a ->groupBy() on top of the Explorer query, remove it now. Layering a manual groupBy onto the corrected query is the fastest way to reintroduce the old behavior on MySQL.
  • DISTINCT + ORDER BY strictness differs by engine. SQLite tolerates ordering by a column absent from the select list under DISTINCT; MySQL (especially with ONLY_FULL_GROUP_BY) does not. Do not conclude the fix is broken because your SQLite dev environment and your MySQL staging environment disagree — check the generated SQL with ->toSql() and the bindings, not just the result set.
  • Empty tables are now tested behavior. If you were guarding against empty results in your own UI layer because a filter returned something odd on an empty audit table, re-check whether that guard is still needed. Return shape on empty input is now part of the package's contract.
  • Authorization boundaries are tested, but yours are not. The package's regression coverage is about its own gating seam. Your policy logic — tenancy scoping, role checks, soft-deleted actors — still needs its own tests. A filter that returns correct rows for an admin tells you nothing about what it returns for a scoped user.
  • Debugging ordering issues: log the query rather than the collection. Chronological bugs almost always live in the ORDER BY clause or in a missing tiebreaker, and dd($results->pluck('occurred_at')) will look correct while pagination is still broken.
  • Upgrade is safe but verify anyway. This is a patch release with no deprecations or removals, so composer update should be uneventful — but bump your lockfile deliberately and re-run your Explorer tests, since the changed behavior is in read paths that feature tests often do not cover.
  • Extension point worth knowing: if the package lets you swap the audit model or repository, do that rather than subclassing query builders to customize ordering. Overriding the model keeps you on the supported path when the ordering logic changes again — which, at 0.1.x, it likely will.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource