Log (HasLog)
File: app/Traits/Common/HasLog.php
A Laravel Eloquent trait that automatically logs model events (create, update, delete) to the activity_logs table — with audit trail including user, IP address, user agent, and old/new values.
Overview
Any model that uses HasLog will automatically create an ActivityLog record whenever a model is created, updated, or deleted — without any additional code in controllers or services.
Why It Exists
- Audit trail: Every change to important models is tracked with who did it and what changed
- Zero-effort: Drop
use HasLogon a model and logging starts automatically — no controller changes needed - Forensic detail: Captures
oldvsnewvalues on updates, so you can see exactly what changed - Context: Logs include IP address and user agent for security auditing
Methods
bootHasLog(): void
Boots the trait and registers three Eloquent event listeners via Event::listen():
| Event | Filter | Description |
|---|---|---|
eloquent.created: {class} | None | Logs created action |
eloquent.updated: {class} | Skips updated_at only | Logs updated action with old/new diff |
eloquent.deleted: {class} | None | Logs deleted action |
The {class} placeholder is resolved at boot time to the fully qualified class name (e.g., App\Models\Recruitment\Candidate), so each model gets its own set of listeners.
Update filter algorithm:
// updated event
$dirty = $model->getDirty();
unset($dirty['updated_at']); // exclude timestamp noise
if (empty($dirty)) return; // nothing meaningful changed
$original = array_intersect_key($model->getRawOriginal(), $dirty);
$model->logActivity('updated', '...', [
'old' => $original, // values before the change
'new' => $dirty, // values after the change
]);activityLogs(): MorphMany
Returns the polymorphic relationship to the ActivityLog model:
public function activityLogs(): MorphMany
{
return $this->morphMany(ActivityLog::class, 'loggable');
}Usage:
$candidate->activityLogs; // all logs for this candidate
$candidate->activityLogs()->latest()->take(10)->get(); // last 10logActivity(string $action, ?string $description = null, ?array $properties = null): ActivityLog
Manually logs an activity. Called automatically by bootHasLog(), but can also be called manually:
public function logActivity(string $action, ?string $description = null, ?array $properties = null): ActivityLog
{
return $this->activityLogs()->create([
'user_id' => Auth::id(),
'action' => $action, // 'created', 'updated', 'deleted', or custom
'description'=> $description, // human-readable summary
'properties' => $properties, // optional metadata (old/new values, etc.)
'ip_address' => Request::ip(),
'user_agent' => Request::userAgent(),
]);
}How to Use
1. Add the trait to your model
use App\Traits\Common\HasLog;
class Candidate extends Model
{
use HasLog;
}2. Logging happens automatically
$candidate = Candidate::create([...]);
// ✓ ActivityLog: "Candidate was created."
$candidate->update(['status' => 'hired']);
// ✓ ActivityLog: "Candidate was updated." with { old: { status: "applied" }, new: { status: "hired" } }
$candidate->delete();
// ✓ ActivityLog: "Candidate was deleted."3. Read activity logs
foreach ($candidate->activityLogs as $log) {
echo $log->action; // "created", "updated", "deleted"
echo $log->description; // "Candidate was updated."
echo $log->user->name; // "Super Admin"
echo $log->ip_address; // "192.168.1.1"
echo $log->properties; // {"old": {"status": "applied"}, "new": {"status": "hired"}}
echo $log->created_at; // 2026-07-30 12:00:00
}4. Manual logging
$candidate->logActivity('status_changed', 'Candidate moved to interview stage', [
'from' => 'screening',
'to' => 'interview',
'by' => 'hr_manager',
]);Key Files
| File | Purpose |
|---|---|
app/Traits/Common/HasLog.php | The trait — automatic event listeners + manual logging |
app/Models/Platform/ActivityLog/ActivityLog.php | The activity_logs model — stores user_id, action, description, properties, ip_address, user_agent |
app/Models/ | Any model that uses use HasLog |