# Activity Logging Track user actions, model changes, and system events with GDPR-compliant activity logging. ## Basic Usage ### Enabling Activity Logging Add the `LogsActivity` trait to your model: ```php log( subject: $post, event: 'published', description: 'Post published to homepage', causer: auth()->user() ); // Log with properties $logger->log( subject: $post, event: 'viewed', properties: [ 'ip_address' => request()->ip(), 'user_agent' => request()->userAgent(), ] ); ``` ## Activity Model ### Retrieving Activity ```php use Core\Activity\Models\Activity; // Get all activity $activities = Activity::latest()->get(); // Get activity for specific model $postActivity = Activity::forSubject($post)->get(); // Get activity by user $userActivity = Activity::causedBy($user)->get(); // Get activity by event $published = Activity::where('event', 'published')->get(); ``` ### Activity Attributes ```php $activity = Activity::latest()->first(); $activity->subject; // The model that was acted upon $activity->causer; // The user who caused the activity $activity->event; // Event name (created, updated, deleted, etc.) $activity->description; // Human-readable description $activity->properties; // Additional data (array) $activity->created_at; // When it occurred ``` ### Relationships ```php // Subject (polymorphic) $post = $activity->subject; // Causer (polymorphic) $user = $activity->causer; // Workspace (if applicable) $workspace = $activity->workspace; ``` ## Activity Scopes ### Filtering Activity ```php use Core\Activity\Models\Activity; // By date range $activities = Activity::query() ->whereBetween('created_at', [now()->subDays(7), now()]) ->get(); // By event type $activities = Activity::query() ->whereIn('event', ['created', 'updated']) ->get(); // By workspace $activities = Activity::query() ->where('workspace_id', $workspace->id) ->get(); // Complex filters $activities = Activity::query() ->forSubject($post) ->causedBy($user) ->where('event', 'updated') ->latest() ->paginate(20); ``` ### Custom Scopes ```php use Core\Activity\Scopes\ActivityScopes; // Add to Activity model class Activity extends Model { use ActivityScopes; public function scopeForWorkspace($query, $workspaceId) { return $query->where('workspace_id', $workspaceId); } public function scopeWithinDays($query, $days) { return $query->where('created_at', '>=', now()->subDays($days)); } } // Usage $recent = Activity::withinDays(7) ->forWorkspace($workspace->id) ->get(); ``` ## Customizing Logged Data ### Controlling What's Logged ```php class Post extends Model { use LogsActivity; // Only log these events protected static $recordEvents = ['created', 'published']; // Exclude these attributes from change tracking protected static $ignoreChangedAttributes = ['views', 'updated_at']; // Log only these attributes protected static $logAttributes = ['title', 'status']; } ``` ### Custom Descriptions ```php class Post extends Model { use LogsActivity; public function getActivityDescription(string $event): string { return match($event) { 'created' => "Created post: {$this->title}", 'updated' => "Updated post: {$this->title}", 'published' => "Published post: {$this->title}", default => "Post {$event}", }; } } ``` ### Custom Properties ```php class Post extends Model { use LogsActivity; public function getActivityProperties(string $event): array { return [ 'title' => $this->title, 'category' => $this->category->name, 'word_count' => str_word_count($this->content), 'published_at' => $this->published_at?->toIso8601String(), ]; } } ``` ## GDPR Compliance ### IP Address Hashing IP addresses are automatically hashed for privacy: ```php use Core\Crypt\LthnHash; // Automatically applied $activity = Activity::create([ 'properties' => [ 'ip_address' => request()->ip(), // Hashed before storage ], ]); // Verify IP match without storing plaintext if (LthnHash::check(request()->ip(), $activity->properties['ip_address'])) { // IP matches } ``` ### Data Retention ```php use Core\Activity\Console\ActivityPruneCommand; // Prune old activity (default: 90 days) php artisan activity:prune // Custom retention php artisan activity:prune --days=30 // Dry run php artisan activity:prune --dry-run ``` **Scheduled Pruning:** ```php // app/Console/Kernel.php protected function schedule(Schedule $schedule) { $schedule->command('activity:prune') ->daily() ->at('02:00'); } ``` ### Right to Erasure ```php // Delete all activity for a user Activity::causedBy($user)->delete(); // Delete activity for specific subject Activity::forSubject($post)->delete(); // Anonymize instead of delete Activity::causedBy($user)->update([ 'causer_id' => null, 'causer_type' => null, ]); ``` ## Activity Feed ### Building Activity Feeds ```php use Core\Activity\Models\Activity; // User's personal feed $feed = Activity::causedBy($user) ->with(['subject', 'causer']) ->latest() ->paginate(20); // Workspace activity feed $feed = Activity::query() ->where('workspace_id', $workspace->id) ->whereIn('event', ['created', 'updated', 'published']) ->with(['subject', 'causer']) ->latest() ->paginate(20); ``` ### Rendering Activity ```blade {{-- resources/views/activity/feed.blade.php --}} @foreach($activities as $activity)
{{ $activity->causer?->name ?? 'System' }} {{ $activity->description }}