1. Go to this page and download the library: Download iamfarhad/laravel-audit-log 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/ */
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class Order extends Model
{
use Auditable;
protected $fillable = ['customer_id', 'total', 'status'];
}
// When a user makes a change via HTTP request
Auth::loginUsingId(1);
$order = Order::create([
'customer_id' => 123,
'total' => 99.99,
'status' => 'pending'
]);
// Audit log will contain:
// - causer_type: "App\Models\User"
// - causer_id: 1
// - source: "App\Http\Controllers\OrderController@store"
// Get audit logs with causer information
$auditLogs = Order::find(1)->auditLogs()
->where('causer_type', 'App\Models\User')
->where('causer_id', 1)
->get();
// Using the EloquentAuditLog model directly
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
$logs = EloquentAuditLog::forEntity(Order::class)
->forCauser('App\Models\User')
->forCauserId(1)
->get();
// Get logs from HTTP requests only
$httpLogs = EloquentAuditLog::forEntity(Order::class)
->fromHttp()
->get();
// Get logs from console commands only
$consoleLogs = EloquentAuditLog::forEntity(Order::class)
->fromConsole()
->get();
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Order;
use Illuminate\Http\Request;
final class OrderController extends Controller
{
public function update(Request $request, Order $order): JsonResponse
{
// User is already authenticated via middleware
// Audit log will automatically capture:
// - causer_type: "App\Models\User"
// - causer_id: Auth::id()
// - source: "App\Http\Controllers\OrderController@update"
$order->update($request->validated());
return response()->json(['success' => true]);
}
}
declare(strict_types=1);
namespace App\Console\Commands;
use App\Models\Order;
use Illuminate\Console\Command;
final class ProcessOrdersCommand extends Command
{
protected $signature = 'orders:process';
public function handle(): void
{
// For console commands, causer_type and causer_id will be null
// But source will be: "orders:process"
Order::where('status', 'pending')
->each(function ($order) {
$order->update(['status' => 'processed']);
});
}
}
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class Order extends Model
{
use Auditable;
protected $fillable = ['customer_id', 'total', 'status'];
/**
* Add user context to audit metadata
*/
public function getAuditMetadata(): array
{
return [
'ip_address' => request()->ip() ?? 'unknown',
'user_agent' => request()->userAgent() ?? 'unknown',
'user_email' => auth()->user()?->email ?? 'system',
'session_id' => session()->getId() ?? null,
'request_id' => request()->header('X-Request-Id', 'n/a'),
];
}
}
declare(strict_types=1);
namespace App\Services;
use App\Models\User;
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
final class AuditReportService
{
public function getUserActivity(User $user, string $action = null): array
{
$query = EloquentAuditLog::forCauser(User::class)
->forCauserId($user->id)
->orderBy('created_at', 'desc');
if ($action) {
$query->forAction($action);
}
return $query->get()->map(function ($log) {
return [
'entity_type' => $log->entity_type,
'entity_id' => $log->entity_id,
'action' => $log->action,
'source' => $log->source,
'created_at' => $log->created_at,
'metadata' => $log->metadata,
];
})->toArray();
}
public function getHttpRequestChanges(string $controller = null): array
{
$query = EloquentAuditLog::fromHttp();
if ($controller) {
$query->fromController($controller);
}
return $query->get()->toArray();
}
public function getConsoleChanges(string $command = null): array
{
$query = EloquentAuditLog::fromConsole();
if ($command) {
$query->fromCommand($command);
}
return $query->get()->toArray();
}
}
'causer' => [
'guard' => null, // null means use default guard, or specify 'api', 'web', etc.
'model' => null, // null means auto-detect, or specify 'App\Models\CustomUser'
'resolver' => null, // null means use default resolver, or specify custom class
],
declare(strict_types=1);
namespace App\Services;
use iamfarhad\LaravelAuditLog\Contracts\CauserResolverInterface;
use Illuminate\Support\Facades\Auth;
final class CustomCauserResolver implements CauserResolverInterface
{
public function resolve(): array
{
// Custom logic for resolving the causer
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
return [
'type' => get_class($user),
'id' => $user->id,
];
}
if (Auth::guard('web')->check()) {
$user = Auth::guard('web')->user();
return [
'type' => get_class($user),
'id' => $user->id,
];
}
// For system operations, you might want to return a system user
return [
'type' => 'System',
'id' => 'system',
];
}
}
// In AppServiceProvider or custom service provider
$this->app->bind(
\iamfarhad\LaravelAuditLog\Contracts\CauserResolverInterface::class,
\App\Services\CustomCauserResolver::class
);
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class User extends Model
{
use Auditable;
protected array $auditExclude = [
'password',
'remember_token',
'email_verified_at',
];
}
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class Invoice extends Model
{
use Auditable;
protected array $auditInclude = [
'amount',
'status',
'due_date',
];
}
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class Transaction extends Model
{
use Auditable;
public function getAuditMetadata(): array
{
return [
'ip_address' => request()->ip() ?? 'unknown',
'user_agent' => request()->userAgent() ?? 'unknown',
'request_id' => request()->header('X-Request-Id', 'n/a'),
'session_id' => session()->getId() ?? null,
];
}
}
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
// Find all changes made by a specific console command
$commandLogs = EloquentAuditLog::forEntity(User::class)
->fromCommand('app:send-emails')
->get();
// Find all changes made through console commands
$consoleLogs = EloquentAuditLog::forEntity(User::class)
->fromConsole()
->get();
// Find all changes made through HTTP requests
$webLogs = EloquentAuditLog::forEntity(User::class)
->fromHttp()
->get();
// Find changes from a specific controller
$controllerLogs = EloquentAuditLog::forEntity(User::class)
->fromController('UserController')
->get();
// Find changes by exact source match
$exactSourceLogs = EloquentAuditLog::forEntity(User::class)
->forSource('app:send-emails')
->get();
// Complex filtering with multiple scopes
$filteredLogs = EloquentAuditLog::forEntity(User::class)
->fromConsole()
->forAction('updated')
->dateBetween(now()->subWeek(), now())
->forCauserId(1)
->orderBy('created_at', 'desc')
->paginate(20);
$user = User::find(1);
$user->disableAuditing();
$user->update(['email' => '[email protected]']); // This change won't be logged
$user->enableAuditing();
// config/audit-logger.php
'queue' => [
'enabled' => true,
'connection' => 'redis', // or your preferred queue connection
'queue_name' => 'audit',
'delay' => 0, // delay in seconds before processing
],
declare(strict_types=1);
namespace App\Audit\Drivers;
use iamfarhad\LaravelAuditLog\Contracts\AuditDriverInterface;
use iamfarhad\LaravelAuditLog\DTOs\AuditLog;
final class CustomAuditDriver implements AuditDriverInterface
{
public function log(AuditLog $auditLog): void
{
// Your custom logging implementation
}
public function createStorageForEntity(string $entityClass): void
{
// Your custom storage creation logic
}
// ... implement other
declare(strict_types=1);
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use iamfarhad\LaravelAuditLog\Contracts\AuditDriverInterface;
use App\Audit\Drivers\CustomAuditDriver;
final class AuditServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(AuditDriverInterface::class, CustomAuditDriver::class);
}
}
declare(strict_types=1);
namespace App\Audit;
use iamfarhad\LaravelAuditLog\Contracts\CauserResolverInterface;
use Illuminate\Database\Eloquent\Model;
final class CustomCauserResolver implements CauserResolverInterface
{
public function resolve(): ?Model
{
// Your custom causer resolution logic
return auth()->user() ?? $this->getSystemUser();
}
private function getSystemUser(): ?Model
{
// Return a system user for automated processes
return User::where('email', '[email protected]')->first();
}
}
$user = User::find(1);
// Retrieve all audit logs
$allLogs = $user->auditLogs()->get();
// Filter by specific action
$updateLogs = $user->auditLogs()->where('action', 'updated')->get();
// Get the most recent logs with pagination
$recentLogs = $user->auditLogs()
->orderBy('created_at', 'desc')
->paginate(10);
// Include metadata and causer information
$detailedLogs = $user->auditLogs()
->with(['causer'])
->orderBy('created_at', 'desc')
->get();
// Example service for audit log maintenance
declare(strict_types=1);
namespace App\Services;
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
final class AuditMaintenanceService
{
public function cleanupOldLogs(int $daysToKeep = 365): int
{
return EloquentAuditLog::where('created_at', '<', now()->subDays($daysToKeep))
->delete();
}
public function getStorageStats(): array
{
return [
'total_logs' => EloquentAuditLog::count(),
'logs_last_30_days' => EloquentAuditLog::dateGreaterThan(now()->subDays(30))->count(),
'most_active_entities' => EloquentAuditLog::selectRaw('entity_type, COUNT(*) as count')
->groupBy('entity_type')
->orderBy('count', 'desc')
->limit(10)
->get(),
];
}
}
declare(strict_types=1);
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
final class UserAuditTest extends TestCase
{
public function test_user_creation_is_audited(): void
{
$user = User::create([
'name' => 'John Doe',
'email' => '[email protected]',
]);
$this->assertDatabaseHas('audit_users_logs', [
'entity_id' => $user->id,
'action' => 'created',
]);
$auditLog = EloquentAuditLog::forEntity(User::class)
->forEntityId($user->id)
->forAction('created')
->first();
$this->assertNotNull($auditLog);
$this->assertEquals('created', $auditLog->action);
$this->assertNotNull($auditLog->new_values);
}
public function test_sensitive_fields_are_excluded(): void
{
$user = User::create([
'name' => 'John Doe',
'email' => '[email protected]',
'password' => bcrypt('secret'),
]);
$auditLog = EloquentAuditLog::forEntity(User::class)
->forEntityId($user->id)
->first();
$newValues = json_decode($auditLog->new_values, true);
$this->assertArrayNotHasKey('password', $newValues);
}
}
// Example policy for audit log access
declare(strict_types=1);
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
final class AuditLogPolicy
{
use HandlesAuthorization;
public function viewAny(User $user): bool
{
return $user->hasPermission('audit.view');
}
public function view(User $user, string $entityType, $entityId): bool
{
return $user->hasPermission('audit.view')
&& $this->canAccessEntity($user, $entityType, $entityId);
}
private function canAccessEntity(User $user, string $entityType, $entityId): bool
{
// Implement your business logic for entity access
return true;
}
}
'entities' => [
\App\Models\User::class => [
'table' => 'users',
'retention' => [
'enabled' => true,
'days' => 730, // Keep for 2 years
'strategy' => 'anonymize',
'anonymize_after_days' => 365, // Anonymize after 1 year
],
],
\App\Models\Order::class => [
'table' => 'orders',
'retention' => [
'days' => 2555, // Keep for 7 years (compliance)
'strategy' => 'archive',
],
],
],
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use iamfarhad\LaravelAuditLog\Traits\Auditable;
final class User extends Model
{
use Auditable;
// Per-model retention configuration
protected array $auditRetention = [
'enabled' => true,
'days' => 730,
'strategy' => 'anonymize',
'anonymize_after_days' => 365,
];
}
'strategy' => 'delete',
'days' => 365, // Delete records older than 1 year
'strategy' => 'anonymize',
'days' => 180, // Anonymize records older than 6 months
'strategy' => 'archive',
'days' => 365, // Archive records older than 1 year
'archive_connection' => 'archive_db',
'strategy' => 'delete', // or 'archive'
'days' => 730, // Final action after 2 years
'anonymize_after_days' => 365, // Anonymize after 1 year
use iamfarhad\LaravelAuditLog\Jobs\RetentionCleanupJob;
// Dispatch cleanup job for all entities
RetentionCleanupJob::dispatch();
// Dispatch cleanup job for specific entity
RetentionCleanupJob::dispatch('App\Models\User');
protected function schedule(Schedule $schedule): void
{
// Run retention cleanup weekly
$schedule->command('audit:cleanup --force')
->weekly()
->environments(['production']);
// Or run for specific entities
$schedule->command('audit:cleanup --entity="App\Models\User" --force')
->daily()
->at('02:00');
}
declare(strict_types=1);
use iamfarhad\LaravelAuditLog\Contracts\RetentionServiceInterface;
use iamfarhad\LaravelAuditLog\DTOs\RetentionConfig;
final class AuditMaintenanceService
{
public function __construct(
private readonly RetentionServiceInterface $retentionService
) {}
public function cleanupUserAudits(): void
{
$config = new RetentionConfig(
enabled: true,
days: 365,
strategy: 'anonymize',
batchSize: 1000,
anonymizeAfterDays: 180
);
$result = $this->retentionService->processRetention(
entityClass: 'App\Models\User',
config: $config,
dryRun: false
);
// Handle results...
}
}
// In your service provider or configuration
use iamfarhad\LaravelAuditLog\Services\AuditLogger;
// Enable debug mode (not recommended for production)
AuditLogger::enableDebugMode();
// This will log additional information about audit processing