logActivity('member.role_changed', ['old_role' => 'member', 'new_role' => 'admin']); */ trait LogsWorkspaceActivity { /** * Boot the trait — optionally auto-log on create/update/delete. */ protected static function bootLogsWorkspaceActivity(): void { if (static::shouldAutoLog()) { static::created(function (Model $model) { $model->logActivityIfWorkspaced('created'); }); static::updated(function (Model $model) { $model->logActivityIfWorkspaced('updated', [ 'changed' => $model->getChanges(), ]); }); static::deleted(function (Model $model) { $model->logActivityIfWorkspaced('deleted'); }); } } /** * Whether this model should auto-log create/update/delete events. * * Override in model to enable: protected static bool $autoLogActivity = true; */ protected static function shouldAutoLog(): bool { return property_exists(static::class, 'autoLogActivity') && static::$autoLogActivity === true; } /** * Log an activity for this model within its workspace context. */ public function logActivity(string $action, ?array $metadata = null, ?User $user = null): WorkspaceActivity { $workspaceId = $this->getActivityWorkspaceId(); return WorkspaceActivity::record( workspace: $workspaceId, action: $action, subject: $this, user: $user, metadata: $metadata, ); } /** * Log activity only if the model has a workspace context. */ protected function logActivityIfWorkspaced(string $suffix, ?array $metadata = null): void { $workspaceId = $this->getActivityWorkspaceId(); if ($workspaceId === null) { return; } $action = static::getActivityActionPrefix() . '.' . $suffix; WorkspaceActivity::record( workspace: $workspaceId, action: $action, subject: $this, metadata: $metadata, ); } /** * Get the workspace ID for this model's activity log. * * Override in model if the workspace relationship differs. */ protected function getActivityWorkspaceId(): ?int { if (property_exists($this, 'workspace_id') || $this->getAttribute('workspace_id')) { return $this->getAttribute('workspace_id'); } // Try to resolve from the current workspace context $workspace = Workspace::current(); return $workspace?->id; } /** * Get the action prefix for auto-logged events. * * Override via: protected static string $activityActionPrefix = 'custom_prefix'; */ protected static function getActivityActionPrefix(): string { if (property_exists(static::class, 'activityActionPrefix')) { return static::$activityActionPrefix; } // Derive from class name: WorkspaceMember -> workspace_member return strtolower((string) preg_replace('/(?