'array', 'activity_logs_new_values' => 'array', 'created_at' => 'datetime', 'updated_at' => 'datetime' ]; /** * The attributes that should be mutated to dates. * * @var array */ protected $dates = [ 'created_at', 'updated_at' ]; /** * Indicates if the model should be timestamped. * * @var bool */ public $timestamps = true; /** * Get the user that performed the activity. */ public function user(): BelongsTo { return $this->belongsTo(\Modules\Auth\Models\User::class, 'activity_logs_user_id'); } /** * Get the company where the activity occurred. */ public function company(): BelongsTo { return $this->belongsTo(Company::class, 'activity_logs_bus_id'); } /** * Scope to get logs for a specific user. * * @param \Illuminate\Database\Eloquent\Builder $query * @param int|null $userId * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForUser($query, ?int $userId) { return $query->where('activity_logs_user_id', $userId); } /** * Scope to get logs for a specific company. * * @param \Illuminate\Database\Eloquent\Builder $query * @param int|null $companyId * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForCompany($query, ?int $companyId) { return $query->where('activity_logs_bus_id', $companyId); } /** * Scope to get logs for a specific action. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $action * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForAction($query, string $action) { return $query->where('activity_logs_action', $action); } /** * Scope to get logs for a specific resource type. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $resourceType * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForResourceType($query, string $resourceType) { return $query->where('activity_logs_resource_type', $resourceType); } /** * Scope to get logs for a specific resource. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $resourceType * @param int $resourceId * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForResource($query, string $resourceType, int $resourceId) { return $query->where('activity_logs_resource_type', $resourceType) ->where('activity_logs_resource_id', $resourceId); } /** * Scope to get logs within a date range. * * @param \Illuminate\Database\Eloquent\Builder $query * @param \DateTime|string $startDate * @param \DateTime|string $endDate * @return \Illuminate\Database\Eloquent\Builder */ public function scopeBetweenDates($query, $startDate, $endDate) { return $query->whereBetween('created_at', [$startDate, $endDate]); } /** * Scope to order logs by creation date. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $direction * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOrderByDate($query, string $direction = 'desc') { return $query->orderBy('created_at', $direction); } /** * Log an activity. * * @param int|null $userId * @param int|null $companyId * @param string $action * @param string $resourceType * @param int|null $resourceId * @param array|null $oldValues * @param array|null $newValues * @param string|null $ipAddress * @param string|null $userAgent * @return static */ public static function log( ?int $userId, ?int $companyId, string $action, string $resourceType, ?int $resourceId = null, ?array $oldValues = null, ?array $newValues = null, ?string $ipAddress = null, ?string $userAgent = null ): self { // Also log to file for monitoring Log::info("Activity: {$action}", [ 'user_id' => $userId, 'company_id' => $companyId, 'resource' => "{$resourceType}:{$resourceId}", 'ip' => $ipAddress ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1') ]); $resolvedCompanyId = $companyId ?? ($_SESSION['current_company_id'] ?? ($_SESSION['user_idbus'] ?? null)); // Validate company id exists to satisfy FK constraints if ($resolvedCompanyId !== null) { $exists = Capsule::table('bus')->where('bus_id', $resolvedCompanyId)->exists(); if (!$exists) { $resolvedCompanyId = null; } } $attributes = [ 'activity_logs_user_id' => $userId, 'activity_logs_bus_id' => $resolvedCompanyId, 'activity_logs_action' => $action, 'activity_logs_resource_type' => $resourceType, 'activity_logs_resource_id' => $resourceId, 'activity_logs_old_values' => $oldValues, 'activity_logs_new_values' => $newValues, 'activity_logs_ip_address' => $ipAddress ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1'), 'activity_logs_user_agent' => $userAgent ?? ($_SERVER['HTTP_USER_AGENT'] ?? 'Console') ]; // If company id is invalid/null and DB enforces NOT NULL or FK, avoid DB insert if ($resolvedCompanyId === null) { return new self($attributes); } return self::create($attributes); } /** * Get the action attribute. * * @return string */ public function getActionAttribute(): string { return $this->activity_logs_action; } /** * Get the resource type attribute. * * @return string */ public function getResourceTypeAttribute(): string { return $this->activity_logs_resource_type; } /** * Get the resource id attribute. * * @return int|null */ public function getResourceIdAttribute(): ?int { return $this->activity_logs_resource_id; } /** * Get the user name attribute. * * @return string */ public function getUserNameAttribute(): string { return $this->user ? ($this->user->users_name ?: $this->user->users_email) : __('activity.unknown_user'); } /** * Get the action color attribute. * * @return string */ public function getActionColorAttribute(): string { $colors = [ 'created' => 'success', 'updated' => 'primary', 'deleted' => 'danger', 'login' => 'success', 'logout' => 'warning', 'password_changed' => 'info', 'profile_updated' => 'info', 'module_enabled' => 'success', 'module_disabled' => 'secondary', 'permission_granted' => 'success', 'permission_revoked' => 'danger' ]; return $colors[$this->activity_logs_action] ?? 'secondary'; } /** * Get the formatted action description. * * @return string */ public function getFormattedActionAttribute(): string { $actions = [ 'created' => __('activity.created'), 'updated' => __('activity.updated'), 'deleted' => __('activity.deleted'), 'login' => __('activity.login'), 'logout' => __('activity.logout'), 'password_changed' => __('activity.password_changed'), 'profile_updated' => __('activity.profile_updated'), 'module_enabled' => __('activity.module_enabled'), 'module_disabled' => __('activity.module_disabled'), 'permission_granted' => __('activity.permission_granted'), 'permission_revoked' => __('activity.permission_revoked') ]; return $actions[$this->activity_logs_action] ?? $this->activity_logs_action; } /** * Get the formatted resource description. * * @return string */ public function getFormattedResourceAttribute(): string { if ($this->activity_logs_resource_type && $this->activity_logs_resource_id) { return "{$this->activity_logs_resource_type} #{$this->activity_logs_resource_id}"; } return $this->activity_logs_resource_type ?: __('activity.unknown_resource'); } /** * Get the formatted changes description. * * @return string */ public function getFormattedChangesAttribute(): string { if (!$this->activity_logs_old_values && !$this->activity_logs_new_values) { return ''; } $changes = []; if ($this->activity_logs_old_values && $this->activity_logs_new_values) { foreach ($this->activity_logs_old_values as $key => $oldValue) { if (isset($this->activity_logs_new_values[$key])) { $newValue = $this->activity_logs_new_values[$key]; $changes[] = "{$key}: '{$oldValue}' → '{$newValue}'"; } } } return implode(', ', $changes); } }