andreshg112/laravel-auditing-notifications
A Laravel package that connects Laravel Auditing with notifications, letting you alert teams when audit events occur. Configure which audits and events trigger notifications so important model changes don’t go unnoticed.
This is a thin adapter between two packages you probably already have: owen-it/laravel-auditing (records changes on Eloquent models) and Laravel's notification system. It registers an audit driver so that instead of (or in addition to) persisting an Audit row, the package hands the audit payload to a notification class you control.
Minimum steps:
composer require owen-it/laravel-auditing andreshg112/laravel-auditing-notifications
Publish the auditing config if you haven't already (php artisan vendor:publish --provider="OwenIt\Auditing\AuditingServiceProvider"). There is no separate migration to publish — the actual audit storage still belongs to owen-it/laravel-auditing, not this package.config/audit.php, specifically the drivers array. This package's value is a new driver key (typically notification or notifications) that you add there. That is the whole integration surface — the rest is standard Laravel notifications.Auditable trait to a model whose changes you care about (e.g. Invoice, User, Order) and enable the events you want to fan out (created, updated, deleted, restored).via() channel that is a mail or Slack channel, write a notification class that renders the audit's old_values / new_values, and point the audit driver at it. You now get a human-readable diff without writing observer boilerplate.Before you build on it, note that the repository is archived and last released in September 2023. Treat it as a reference implementation rather than a maintained dependency — the pattern is easy to reproduce yourself (see Gotchas).
The core pattern: audit payload → notification class. The package's job is to call Notification::send(...) with the Audit model (or an array of audit attributes) as the notification's data. Your notification class is plain Laravel:
class AuditNotification extends Notification
{
public function __construct(protected array $audit) {}
public function via($notifiable): array
{
return ['mail', 'slack'];
}
public function toMail($notifiable)
{
return (new MailMessage)
->subject("Audit: {$this->audit['event']} on {$this->audit['auditable_type']}")
->line(json_encode($this->audit['new_values'], JSON_PRETTY_PRINT));
}
public function toSlack($notifiable)
{
return (new SlackMessage)->content(
"{$this->audit['event']} — ".class_basename($this->audit['auditable_type'])
);
}
}
Keep the notification logic dumb. The package's real leverage is that the audit config decides when it fires — you don't wire up observers per model.
Routing per model, not per audit. Laravel's notification routing (via, toMail, routeNotificationForSlack) is where you'll actually spend time. A useful workflow is one notification class per domain concern (SecurityAuditNotification for User/Role models, BillingAuditNotification for Invoice/Subscription) rather than one per model. Switch on $audit['auditable_type'] inside via() or toArray() if channels differ.
Queueing. Laravel notifications implement ShouldQueue — add it to your audit notification to keep audit writes off the request path. This matters because auditing already adds a created/updated write; you don't want the notification dispatch adding latency on top. If you need the notification not to block, ShouldQueue + queue driver is the standard escape hatch.
Integration with Notification::route() for dynamic recipients. Since audit data includes the model ID and type, a common pattern is resolving recipients at send time (e.g. the invoice owner, or a SlackWebhook from config('services.slack.audit_webhook')) rather than hardcoding channels in the notification. This decouples "who gets notified" from "what changed."
Filtering. The auditing package's audit.console and audit.empty_values/audit.allowed_actions config options still apply — use them to drop noisy retrieved events before they ever reach the notification driver. Filtering at the config layer is cheaper than filtering in via().
owen-it/laravel-auditing and Laravel major. If it doesn't, the whole thing is ~100 lines to replicate: a custom driver class implementing OwenIt\Auditing\Contracts\Auditor that calls your notification, registered in config/audit.php. That's usually safer than pinning.database driver — your notifications just never fire and you'll blame the notification class. Validate config('audit.drivers') early. Conversely, a driver misconfiguration can throw on every model save; wrap the first integration in a test that saves a model and asserts Notification::fake() received the audit.Notification::fake() is the debugging tool. If you're unsure whether the audit driver is even reached, fake the notification and assert on the class, not the channels. This isolates "driver fired" from "channel failed," which are the two things that go wrong.Audit model's old_values/new_values are JSON casts; in a queued notification they serialize fine, but if you pass the Audit model rather than an array, ensure the serialization of Illuminate\Notifications\Notification keeps the model attributes. Passing an explicit $audit->toArray() is more predictable in queued contexts.retrieved noise. By default the auditing package can fire on a lot of events. Set audit.allowed_actions or filter event === 'retrieved' in the driver, otherwise you'll page your team on a select query.NotificationChannel contract, not this package. Register additional channels (Teams, PagerDuty, etc.) via Notification::extend() or a channel class implementing OwenIt\Auditing\Contracts\... — the package doesn't own channel logic, so you're not limited by it.dev-main of an unmaintained repo.How can I help you explore Laravel packages today?