tapp/filament-auditing
A Filament plugin for Laravel Auditing that adds an audits relation manager to your Filament resources, displays audit tables on view and edit pages, and allows restoring previous audit versions. Supports Filament 4.x/5.x.
owen-it/laravel-auditing. Nothing appears in the panel until your models actually produce audit records. Install that package, publish/run its audits migration, and add the Auditable trait to the models you care about:use OwenIt\Auditing\Contracts\Auditable;
class Article extends Model implements Auditable
{
use \OwenIt\Auditing\Auditable;
}
^4.0 targets Filament 4.x/5.x, while Filament 3.x needs the 3.x branch:composer require tapp/filament-auditing:"^4.0"
use Tapp\Filament\Auditing\RelationManagers\AuditsRelationManager;
public static function getRelations(): array
{
return [
AuditsRelationManager::class,
];
}
Edit a record in the panel, save, and the audits tab now shows the change. If that works, you have the whole loop wired.
use Tapp\FilamentAuditing\FilamentAuditingPlugin;
return $panel->plugins([
FilamentAuditingPlugin::make(),
]);
php artisan vendor:publish --tag="filament-auditing-config"
php artisan vendor:publish --tag="filament-auditing-views"
php artisan vendor:publish --tag="filament-auditing-translations"
resources/css/filament/admin/theme.css), otherwise the plugin's classes get purged and the audit table renders unstyled:@source '../../../../vendor/tapp/filament-auditing';
Where to look first: config/filament-auditing.php (sorting, lazy, extend, mapping, tenancy), then the AuditsRelationManager, then the published tables/columns/key-value.blade.php.
Per-resource audit trail (the default pattern). Drop AuditsRelationManager::class into getRelations() for models where users need “who changed what.” Because it is a relation manager, it inherits the resource's access control and gets the owner record for free—no custom query wiring.
Central audit browser for support/debugging. Register FilamentAuditingPlugin::make() and use the generated AuditResource for ops staff who need to search across models. Restrict it with a policy or by overriding the resource in the resources config key, since a global audit list is a wide blast radius for a normal editor role.
Restore as a “revert this change” button. The restore action on the audits table writes the audit's old values back onto the model. Good for undoing a bad edit to a text field or status. Treat it as a content-level revert, not an event rollback: it does not undo side effects (sent emails, dispatched jobs, stock movements), and reverting an old audit on a record that has since changed several times will discard the intervening changes. For anything financial or state-machine-like, build an explicit domain-level undo action instead and keep restore off that table.
Ordering and volume control. Set a sensible default order in config rather than per-table:
'audits_sort' => ['column' => 'created_at', 'direction' => 'desc'],
'is_lazy' => true,
'grouped_table_actions' => false,
Keep is_lazy on for large audit tables—on heavily-edited records the audits tab can otherwise issue a heavy query just to render the page.
Foreign key readability via mapping. The default table shows raw IDs for old_values/new_values. Map them once and every audit view improves:
'mapping' => [
'user_id' => ['model' => App\Models\User::class, 'field' => 'name', 'label' => 'User'],
],
This needs published views to take effect.
Per-model formatting with formatAuditFieldsForPresentation($field, $record). When mapping is not enough (money formatting, enums, HTML rendering), implement this method on the auditable model. It receives old_values or new_values plus the audit record and returns a string or HtmlString. This keeps presentation logic next to the model's own domain rules instead of in a Blade file—prefer it over custom_audits_view unless you need to restructure the layout.
Extra columns via audits_extend. Prepend columns that are not in the base table (e.g. a computed URL or a differently formatted created_at):
'audits_extend' => [
'created_at' => [
'class' => \Filament\Tables\Columns\TextColumn::class,
'methods' => ['sortable', 'date' => ['Y-m-d H:i:s', 'America/New_York']],
],
],
Multi-argument methods are expressed as 'method' => [arg1, arg2]; zero-argument methods are just the method name string.
Multi-tenancy. If the panel is tenant-aware, enable tenancy in config before migrating, publish the plugin migration, and run it—it adds the tenant column to audits and the plugin auto-registers the resolver (you do not touch config/audit.php resolvers). Alignment matters: relationship_name and column should match what you set via ->tenantOwnershipRelationshipName() in the panel provider.
Restricting who sees audits. The relation manager does not invent its own authorization layer; use Filament's standard canView()/policy mechanisms on the relation manager or resource rather than relying on the tab being “hard to find.”
Auditable trait, or an audit driver/console configuration that never fires. Verify with php artisan tinker that rows land in audits before debugging Filament.audits_extend (or any config), run php artisan optimize / php artisan config:clear. Silent no-ops after editing config are almost always config cache.mapping and view overrides require published views. If you set mapping and see no change, the views were never published.custom_audits_view is all-or-nothing. Setting it to true hands the whole content area to tables/custom-audit-content.blade.php; you lose the built-in layout and are responsible for the table markup, headers, and empty state. Budget for it.searchable on an extended column searches the audits table column, not a joined relation—do not expect it to find a user by name through audits_extend.audits accumulates unboundedly; on high-write models plan retention/pruning (Laravel Auditing's own threshold/audit pruning options) before the tab becomes slow. is_lazy masks the symptom, not the cause.user_id, is_published) unless you add mapping or per-model formatting—worth doing before demoing to stakeholders.resources.AuditResource in config to change navigation grouping, permissions, or infolist layout without forking the package. Same idea for views: prefer the published override path over editing vendor files.How can I help you explore Laravel packages today?