Download the PHP package soullessthread/laravel-audit-log without Composer
On this page you can find all versions of the php package soullessthread/laravel-audit-log. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package laravel-audit-log
Laravel Audit Log
Database-level trigger-based audit logging for Laravel.
Changes are captured entirely inside the database engine — PHP application code does not intercept Eloquent events. This means bulk updates, raw queries, and changes made by other services are all captured automatically.
Supported databases: MySQL 5.7.8+, MariaDB 10.2+, PostgreSQL 13+, SQL Server 2016+
Requirements
| Dependency | Version |
|---|---|
| PHP | 8.1+ |
| Laravel | 10, 11, or 12 |
| MySQL | 5.7.8+ (native JSON required) |
| MariaDB | 10.2+ |
| PostgreSQL | 13+ — or 12 with CREATE EXTENSION IF NOT EXISTS pgcrypto |
| SQL Server | 2016+ (SESSION_CONTEXT and FOR JSON) |
Installation
Publish the config and migration:
Quick start
1. Add the trait to your model
2. Register the middleware
Add SetAuditContext after your authentication middleware so that the authenticated user is available:
3. Sync database triggers
Run this whenever you add or change a HasAuditLog model:
Options:
Many-to-many (pivot table) logging
audit:sync can also create triggers on pivot tables for BelongsToMany relations. Declare the relation method names in $auditRelations on your model.
Simple form — log only the two FK columns on attach/detach
Running audit:sync now creates INSERT and DELETE triggers on both role_user and permission_user.
Extended form — log extra pivot columns and/or update events
What gets logged
| Field | Value |
|---|---|
table_name |
The pivot table (e.g. role_user) |
row_id |
Value of the FK pointing to this model (e.g. user_id value) |
event_type |
created (attach), deleted (detach), or updated (pivot update) |
old_values |
All audited pivot columns before the change (null on attach) |
new_values |
All audited pivot columns after the change (null on detach) |
changed_columns |
List of columns that actually changed (attach/detach = all columns) |
Querying pivot logs
Notes
- Trigger naming: Pivot triggers use the suffix
_auditlog_piv_{event}to avoid conflicts with regular model triggers on the same table. - Deduplication: If two models declare the same pivot table in their
$auditRelations,audit:syncmerges their column lists and event sets, then creates a single set of triggers. - No single PK: Pivot tables typically have no surrogate key.
row_idstores only the FK to the declaring model; both FK values appear inold_values/new_values. - Only
BelongsToManyis supported: Declaring a relation that is not aBelongsToManycausesaudit:syncto print a warning and skip it.
Querying audit logs
Column-level diff
Restoring a previous state
The restore fires another
UPDATE, which produces its own audit record — creating a full trail of undo operations.
Audit log table schema
| Column | Type | Description |
|---|---|---|
id |
UUID | Generated by the trigger |
table_name |
VARCHAR(100) | Source table |
row_id |
VARCHAR(36) | PK of the changed row (works with int and UUID PKs) |
event_type |
ENUM | created, updated, or deleted |
old_values |
JSON | Column snapshot before the change (null on create) |
new_values |
JSON | Column snapshot after the change (null on delete) |
changed_columns |
JSON | Array of columns that actually differed |
user_id |
VARCHAR(36) | Authenticated user ID |
user_type |
VARCHAR(100) | Auth guard model class (supports multiple guards) |
session_uuid |
CHAR(36) | Groups all changes from one request / command / job |
source |
VARCHAR(255) | route:name, command:name, or job:ClassName |
tags |
JSON | Extra context (e.g. tenant ID for multi-tenant apps) |
created_at |
TIMESTAMP | When the change was recorded |
Artisan commands
audit:sync
Generate or regenerate triggers for all HasAuditLog models.
audit:prune
Delete audit records older than the configured retention window.
Schedule it in your console kernel:
Multi-tenant support (tags)
Inject per-tenant context early in the request lifecycle — before any queries fire:
Or manually at any point during a request:
Console commands and queue jobs
Audit context is injected automatically via event listeners registered in the service provider:
- Artisan commands → source is
command:{name} - Queue jobs → source is
job:{ClassName}
No additional code is needed in your job or command classes.
Authenticated user in queue jobs
Queue workers run in a separate process where auth()->user() is always null. The package solves this automatically using Laravel's Queue::createPayloadUsing() hook:
- At dispatch time (inside the HTTP request, where auth is available) the package appends
audit_user_idandaudit_user_typeto the raw job payload. - When the worker picks up the job the package reads those values from the payload and initialises
AuditContextwith them before the job'shandle()method runs.
This means every database write made inside a queued job is attributed to the user who dispatched it — no changes to your job classes required.
The guard resolution follows the same priority as the SetAuditContext middleware:
- Guards listed in
config('audit-log.auth_guards'), first match wins. - Laravel's default guard as a final fallback.
Note: If a job is dispatched from a console command or from another job (where there is no authenticated user),
audit_user_idandaudit_user_typewill both benullin the log — this is expected and handled gracefully.
To manually override context inside a job (e.g. for a scheduled command that acts on behalf of a specific user):
Full-row snapshots on update (auditLogAll)
By default the UPDATE trigger is selective: it only stores the columns that actually changed in old_values and new_values, keeping audit rows compact. When you need the full row context for every update — for example, to reconstruct the exact state at any point in time without cross-referencing other tables — enable $auditLogAll:
Behaviour with auditLogAll = true:
| Field | Default (false) |
Full-snapshot (true) |
|---|---|---|
old_values |
Only columns that changed | All audited columns |
new_values |
Only columns that changed | All audited columns |
changed_columns |
Columns that actually changed | Same — still only what changed |
| Fires when nothing changed? | No | No — a no-op UPDATE still produces no log row |
The property is optional. Omitting it entirely (or not defining
$auditLogAllon the model at all) is equivalent tofalseand keeps the default compact behaviour.
After adding or changing this property, re-run php artisan audit:sync to regenerate the trigger.
Per-model audit tables
When a model declares protected ?string $auditTable = 'invoice_audit_logs', the generated trigger writes to that table instead of the shared audit_logs. You must create the custom table manually:
The schema is identical to the default audit_logs table.
Soft deletes
Soft deletes are UPDATE statements (setting deleted_at). The update trigger will capture the change if deleted_at is included in the audited columns (it is included by default when using '*', unless added to global_exclude_columns).
To find soft-deleted records in your audit log:
Multiple auth guards
By default the middleware checks only Laravel's default guard. When your application uses several guards (e.g. web, api, a separate admin guard) you can tell the package which ones to check and in what order.
Global config
Set auth_guards in config/audit-log.php to an ordered array. The middleware iterates the list and stops at the first guard that has an authenticated user:
Per-route override (middleware parameter)
Pass guard names directly to the middleware using the standard Laravel colon syntax. Route-level params take full priority over auth_guards in config:
Resolution order summary
| Scenario | Guards checked |
|---|---|
No params, auth_guards is [] |
Default guard only |
No params, auth_guards is ['web','api'] |
web → api (first with user wins) |
Params :api |
api only (config is ignored) |
Params :admin,web |
admin → web (config is ignored) |
Configuration reference
How it works
Because the trigger runs inside the same database transaction as the original statement, if the statement rolls back, the audit record rolls back too — ensuring perfect consistency.
Known limitations
- Composite primary keys are not supported. Only single-column PKs.
- PostgreSQL 12 and below: install
pgcryptoextension (CREATE EXTENSION IF NOT EXISTS pgcrypto) sincegen_random_uuid()is not built-in until PostgreSQL 13. - SQL Server below 2016:
SESSION_CONTEXTandFOR JSONare not available; upgrade to SQL Server 2016+.
License
MIT
All versions of laravel-audit-log with dependencies
illuminate/support Version ^10.0|^11.0|^12.0
illuminate/database Version ^10.0|^11.0|^12.0
illuminate/console Version ^10.0|^11.0|^12.0
illuminate/http Version ^10.0|^11.0|^12.0
illuminate/queue Version ^10.0|^11.0|^12.0