PHP code example of softartisan / laravel-audit-events

1. Go to this page and download the library: Download softartisan/laravel-audit-events library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

softartisan / laravel-audit-events example snippets


// Before
use SoftArtisan\LaravelModelAudits\Concerns\IsAuditable;
config('model-audits.table_name');

// After
use SoftArtisan\LaravelAuditEvents\Concerns\IsAuditable;
config('audit-events.table_name');

// config/audit-events.php
'table_name' => 'model_audits',

use SoftArtisan\LaravelAuditEvents\Concerns\IsAuditable;

class Invoice extends Model
{
    use IsAuditable;
}

$invoice->audits()->get();               // all audits for this model
$invoice->audits()->latest()->first();   // most recent audit

// Filtered by event type
$invoice->getCreatedHistory()->get();
$invoice->getUpdatedHistory()->get();
$invoice->getDeletedHistory()->get();
$invoice->getRestoredHistory()->get();
$invoice->getAuditHistory('invoice.sent')->get(); // any event name

// Diff between old and new values on an update audit
$audit = $invoice->getUpdatedHistory()->latest()->first();
$diff  = $audit->getDiff();
// ['amount' => ['old' => 100, 'new' => 250]]

use SoftArtisan\LaravelAuditEvents\Models\ModelAudit;

// Filter by event name (CRUD or semantic).
ModelAudit::whereEvent('asset.status_changed')->get();

// Filter by a key inside the JSON `context` column (portable across
// MySQL / PostgreSQL / SQLite via Laravel's `->` JSON path operator).
ModelAudit::whereContext('mission_id', 42)->get();

// Filter by the anchored model instance (uses the indexed morph columns).
ModelAudit::forAuditable($invoice)->get();

// Compose them freely.
ModelAudit::forAuditable($asset)
    ->whereEvent('asset.status_changed')
    ->whereContext('mission_id', 42)
    ->latest('created_at')
    ->get();

$audit = $invoice->audits()->latest()->first();
$audit->restore(); // forceFills old_values back onto the model and saves

'global_hidden' => [
    'password',
    'password_confirmation',
    'remember_token',
    'secret',
    'credit_card_number',
],

class Patient extends Model
{
    use IsAuditable;

    public function getHiddenForAudit(): array
    {
        return array_merge(parent::getHiddenForAudit(), [
            'ssn',
            'date_of_birth',
            'medical_record_number',
        ]);
    }
}

use SoftArtisan\LaravelAuditEvents\AuditContext;

class ImportInvoicesJob implements ShouldQueue
{
    public function __construct(
        private readonly int $userId,
        private readonly string $batchId,
    ) {}

    public function handle(): void
    {
        AuditContext::actingAs($this->userId, [
            'source'   => 'import-job',
            'batch_id' => $this->batchId,
        ]);

        // All audits produced here will carry $this->userId as causer
        Invoice::create([...]);
        Invoice::find(42)->update([...]);

        AuditContext::reset(); // Always reset — prevents context bleed
    }
}

use SoftArtisan\LaravelAuditEvents\Models\ModelAudit;

// Authentication events
ModelAudit::record('user.logged_in',  ['ip' => request()->ip()], $user->id);
ModelAudit::record('user.logged_out', [], $user->id);

// Bulk operations
ModelAudit::record('csv.exported', [
    'resource' => 'fixed-assets',
    'count'    => 1500,
    'tenant'   => tenant('id'),
], $this->userId);

// Report generation
ModelAudit::record('pdf.generated', [
    'template'   => 'annual-report',
    'invoice_id' => $invoice->id,
], auth()->id());

ModelAudit::record(
    string          $event,
    array           $context   = [],
    int|string|null $causerId  = null,
): ModelAudit

// Invoice sent to client
$invoice->saveHistory(
    event:     'invoice.sent',
    oldValues: [],
    newValues: [],
    context:   ['recipient' => '[email protected]', 'channel' => 'email'],
);

// Status transition with diff
$invoice->saveHistory(
    event:     'invoice.approved',
    oldValues: ['status' => 'draft'],
    newValues: ['status' => 'approved'],
    context:   ['approver_id' => auth()->id()],
);

public function saveHistory(
    string $event,
    array  $oldValues = [],
    array  $newValues = [],
    array  $context   = [],
): void

use SoftArtisan\LaravelAuditEvents\Concerns\IsAuditable;
use SoftArtisan\LaravelAuditEvents\Concerns\TracksRelationChanges;

class Role extends Model
{
    use IsAuditable, TracksRelationChanges;
}

class RoleService
{
    public function syncPermissions(Role $role, array $permissionIds): void
    {
        $before = $role->permissions->pluck('name')->all();

        $role->permissions()->sync($permissionIds);

        $after = $role->fresh()->permissions->pluck('name')->all();

        $role->recordRelationAudit('permissions', $before, $after, [
            'actor_id' => auth()->id(),
        ]);
    }
}

// Before: settings = ['theme' => 'light', 'notifications' => ['email' => true, 'sms' => false]]
// After:  settings = ['theme' => 'dark',  'notifications' => ['email' => true, 'sms' => true]]

$diff = $audit->getDiff();
// [
//   'settings' => [
//     'old'  => ['theme' => 'light', 'notifications' => ['email' => true, 'sms' => false]],
//     'new'  => ['theme' => 'dark',  'notifications' => ['email' => true, 'sms' => true]],
//     'diff' => [
//       'theme'         => ['old' => 'light', 'new' => 'dark'],
//       'notifications' => [
//         'old'  => ['email' => true, 'sms' => false],
//         'new'  => ['email' => true, 'sms' => true],
//         'diff' => ['sms' => ['old' => false, 'new' => true]],
//       ],
//     ],
//   ],
// ]

'json_diff' => [
    'enabled'   => true,
    'max_depth' => 3,
],

'integrity' => [
    'enabled'   => true,
    'key'       => null,       // null uses APP_KEY. Set a dedicated AUDIT_SIGNING_KEY for isolation.
    'algorithm' => 'sha256',   // Any PHP hash_hmac() algorithm
],

'integrity' => [
    'enabled' => true,
    'key'     => env('AUDIT_SIGNING_KEY'),
],

$audit = Invoice::find(1)->audits()->latest()->first();

$audit->isSigned();       // true if the record has a non-null signature
$audit->verifySignature(); // true if the HMAC matches; false if tampered

'archive' => [
    'enabled'            => true,
    'archive_after_days' => 90,         // Records older than 90 days
    'driver'             => 'database', // 'database' | 'json_file'
    'table_name'         => 'audit_events_archive',
    'path'               => null,       // Required for json_file driver; null = storage_path('audit-archives')
],

// bootstrap/app.php
use Illuminate\Console\Scheduling\Schedule;

->withSchedule(function (Schedule $schedule) {
    $schedule->command('audit-events:archive')->weekly();
})

'archive' => [
    'enabled' => true,
    'driver'  => 'json_file',
    'path'    => storage_path('audit-archives'), // or /mnt/cold-storage
],

'pruning' => [
    'enabled'      => true,
    'keep_for_days' => 365,
],

Schedule::command('model:prune', [
    '--model' => [\SoftArtisan\LaravelAuditEvents\Models\ModelAudit::class],
])->daily();

// Tenant A — standard
config(['audit-events.pruning.keep_for_days' => 365]);

// Tenant B — financial compliance (7 years)
config(['audit-events.pruning.keep_for_days' => 2555]);

// config/audit-events.php

return [

    // ── Database ──────────────────────────────────────────────────────────────

    'table_name'  => 'audit_events',
    'model_class' => \SoftArtisan\LaravelAuditEvents\Models\ModelAudit::class,

    // ── Column Mapping ────────────────────────────────────────────────────────
    //
    // morph_type options: 'string' (recommended), 'integer', 'uuid', 'ulid'

    'table_fields' => [
        'id'           => 'audit_id',
        'user_id'      => 'user_id',
        'event'        => 'event',
        'morph_prefix' => 'auditable',
        'morph_type'   => 'string',
        'url'          => 'url',
        'ip_address'   => 'ip_address',
        'user_agent'   => 'user_agent',
        'old_values'   => 'old_values',
        'new_values'   => 'new_values',
        'context'      => 'context',
    ],

    // ── Behaviour ─────────────────────────────────────────────────────────────

    'audit_on_create'  => true,
    'audit_on_update'  => true,

    // true  → remove all audits when a model is hard-deleted
    // false → keep audits and record a final "deleted" entry
    'remove_on_delete' => true,

    // Automatic Eloquent events whitelist.
    // saveHistory() and ModelAudit::record() always bypass this list.
    'events' => ['created', 'updated', 'deleted', 'restored'],

    // ── Security ──────────────────────────────────────────────────────────────

    'global_hidden' => [
        'password',
        'password_confirmation',
        'remember_token',
        'secret',
        'credit_card_number',
    ],

    // ── Deep JSON Diff ────────────────────────────────────────────────────────

    'json_diff' => [
        'enabled'   => true,
        'max_depth' => 3,
    ],

    // ── User Resolver ─────────────────────────────────────────────────────────

    'user' => [
        'guards'   => ['web', 'api', 'sanctum'],
        'resolver' => null, // callable — return the authenticated user instance
    ],

    // ── Pruning ───────────────────────────────────────────────────────────────

    'pruning' => [
        'enabled'       => false,
        'keep_for_days' => 365,
    ],

    // ── Cryptographic Integrity ───────────────────────────────────────────────

    'integrity' => [
        'enabled'   => false,
        'key'       => null,       // null falls back to APP_KEY
        'algorithm' => 'sha256',
    ],

    // ── Archiving ─────────────────────────────────────────────────────────────

    'archive' => [
        'enabled'            => false,
        'archive_after_days' => 90,
        'driver'             => 'database', // 'database' | 'json_file'
        'table_name'         => 'audit_events_archive',
        'path'               => null,       // null → storage_path('audit-archives')
    ],
];

// tests/TestCase.php
protected function defineEnvironment($app): void
{
    $app['config']->set('audit-events.integrity.enabled', false);
}

$app['config']->set('audit-events.integrity.enabled', true);
$app['config']->set('app.key', 'base64:'.base64_encode(str_repeat('x', 32)));
bash
php artisan vendor:publish --tag="laravel-audit-events-config"
php artisan migrate
bash
php artisan vendor:publish --tag="laravel-audit-events-config"
php artisan migrate
bash
php artisan migrate
# Applies: create_audit_events_archive_table

storage/audit-archives/audit_events_archive_2025_03_29.jsonl
storage/audit-archives/audit_events_archive_2025_03_30.jsonl
bash
# Preview without changes
php artisan audit-events:archive --dry-run

# Override threshold
php artisan audit-events:archive --days=365

# Override driver
php artisan audit-events:archive --driver=json_file

# Limit to one model type
php artisan audit-events:archive --model="App\Models\Invoice"

# Custom batch size
php artisan audit-events:archive --chunk=1000
bash
php artisan audit-events:stats

Archive
  Archived records : 18 432
  Oldest archived  : 2024-01-03 09:12:00
  Newest archived  : 2025-12-31 23:59:00