AuditSanitizer::clean($model->getAttributes()) ]); }); // 2. Observer: Updated static::updated(function (Model $model) { $old = []; $new = []; // getDirty() returns an array of key => newValue for changed attributes only foreach ($model->getDirty() as $key => $value) { // Skip 'updated_at' if you don't want it cluttering logs if ($key === 'updated_at') continue; $old[$key] = $model->getOriginal($key); $new[$key] = $value; } // Only log if there are meaningful changes if (!empty($old) || !empty($new)) { self::logAuditEvent($model, 'updated', [ 'old' => AuditSanitizer::clean($old), 'new' => AuditSanitizer::clean($new) ]); } }); // 3. Observer: Deleted static::deleted(function (Model $model) { self::logAuditEvent($model, 'deleted', [ 'attributes' => AuditSanitizer::clean($model->getAttributes()) ]); }); } /** * Helper to construct and send the log payload. * * @param Model $model * @param string $event * @param array $payload */ protected static function logAuditEvent(Model $model, $event, array $payload) { try { $user = Auth::user(); $logData = [ 'event' => $event, 'model' => get_class($model), 'model_id' => $model->getKey(), 'user_id' => $user ? $user->getAuthIdentifier() : null, 'ip' => request()->ip(), // Helper works in L5.5 'url' => request()->fullUrl(), 'changes' => $payload, // 'stream' => 'db-audit' // Distinct stream for DB changes 'stream' => env('CLOUDWATCH_CENTRAL_STREAM', 'centralized-admin-audit-logs') ]; // Send to our custom audit logger dispatch(new ProcessAuditLog('DB Mutation Audit', $logData)); } catch (\Exception $e) { // Fallback to standard log if audit fails Log::error('Auditable Trait Error: ' . $e->getMessage()); } } }