doctor-appointment-system/app/Models/ActivityLogModel.php
2026-04-17 11:13:04 +05:30

220 lines
8.3 KiB
PHP

<?php
namespace App\Models;
use CodeIgniter\Model;
class ActivityLogModel extends Model
{
protected $table = 'activity_logs';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['ip', 'action', 'description', 'activity_user_id', 'activity_user_type', 'target_user_id', 'target_user_type', 'activity_page', 'activity_at'];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'activity_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
public function log(string $action, ?string $description = null, ?string $targetType = null, ?int $targetId = null, ?int $actorId = null, ?string $actorRole = null, ?string $ipAddress = null, ?string $userAgent = null): bool
{
$actorId = $actorId ?? session()->get('id');
$actorRole = $actorRole ?? session()->get('role') ?? 'guest';
$request = service('request');
$ipAddress = $ipAddress ?? ($request->hasHeader('X-Forwarded-For') ? trim(explode(',', $request->getHeaderLine('X-Forwarded-For'))[0]) : $request->getIPAddress());
$userAgent = $userAgent ?? ($request->hasHeader('User-Agent') ? $request->getHeaderLine('User-Agent') : null);
$validTargetTypes = ['admin', 'doctor', 'patient'];
$targetUserType = in_array($targetType, $validTargetTypes, true) ? $targetType : null;
$data = [
'ip' => $ipAddress,
'action' => $action,
'description' => $description,
'activity_user_id' => $actorId,
'activity_user_type' => $actorRole,
'target_user_id' => $targetId,
'target_user_type' => $targetUserType,
'activity_page' => $request->getPath(),
'activity_at' => date('Y-m-d H:i:s'),
];
try {
$result = $this->insert($data);
if (!$result) {
log_message('error', 'Failed to insert activity log: ' . json_encode($data) . ' - Errors: ' . json_encode($this->errors()));
}
return (bool) $result;
} catch (\Exception $e) {
log_message('error', 'Exception in ActivityLogModel::log: ' . $e->getMessage());
return false;
}
}
public function getFilteredLogs(array $filters = [], int $limit = 200): array
{
$builder = $this->select('activity_logs.*, COALESCE(NULLIF(CONCAT(users.first_name, " ", users.last_name), " "), users.email, "Guest") AS actor_name')
->join('users', 'users.id = activity_logs.activity_user_id', 'left');
// Apply filters
if (!empty($filters['search'])) {
$search = $filters['search'];
$builder->groupStart()
->like('users.first_name', $search)
->orLike('users.last_name', $search)
->orLike('users.email', $search)
->orLike('activity_logs.description', $search)
->groupEnd();
}
if (!empty($filters['action'])) {
$builder->where('activity_logs.action', $filters['action']);
}
if (!empty($filters['user_type'])) {
$builder->where('activity_logs.activity_user_type', $filters['user_type']);
}
if (!empty($filters['date_from'])) {
$builder->where('activity_logs.activity_at >=', $filters['date_from'] . ' 00:00:00');
}
if (!empty($filters['date_to'])) {
$builder->where('activity_logs.activity_at <=', $filters['date_to'] . ' 23:59:59');
}
if (!empty($filters['ip'])) {
$builder->where('activity_logs.ip', $filters['ip']);
}
return $builder->orderBy('activity_logs.activity_at', 'DESC')
->findAll($limit);
}
public function getAvailableActions(): array
{
$rows = $this->select('action')
->distinct()
->orderBy('action', 'ASC')
->findAll();
return array_column($rows, 'action');
}
public function getActionSummary(): array
{
$rows = $this->select('action, COUNT(*) AS count')
->groupBy('action')
->orderBy('count', 'DESC')
->findAll();
return array_column($rows, 'count', 'action');
}
public function getRoleSummary(): array
{
$rows = $this->select('activity_user_type AS actor_role, COUNT(*) AS count')
->groupBy('activity_user_type')
->orderBy('count', 'DESC')
->findAll();
return array_column($rows, 'count', 'actor_role');
}
public function getSummary(string $period = '7_days'): array
{
$period = $period === '30_days' ? '30_days' : '7_days';
$startDate = $period === '30_days' ? date('Y-m-d H:i:s', strtotime('-30 days')) : date('Y-m-d H:i:s', strtotime('-7 days'));
return [
'total_actions' => (int) $this->where('activity_at >=', $startDate)->countAllResults(false),
'by_action' => $this->getActionSummaryForPeriod($startDate),
'by_role' => $this->getRoleSummaryForPeriod($startDate),
'most_active_users' => $this->getMostActiveUsers($startDate),
'unique_ips' => $this->getUniqueIPs($startDate),
];
}
protected function getActionSummaryForPeriod(string $startDate): array
{
$rows = $this->select('action, COUNT(*) AS count')
->where('activity_at >=', $startDate)
->groupBy('action')
->orderBy('count', 'DESC')
->findAll();
return array_column($rows, 'count', 'action');
}
protected function getRoleSummaryForPeriod(string $startDate): array
{
$rows = $this->select('activity_user_type AS actor_role, COUNT(*) AS count')
->where('activity_at >=', $startDate)
->groupBy('activity_user_type')
->orderBy('count', 'DESC')
->findAll();
return array_column($rows, 'count', 'actor_role');
}
protected function getMostActiveUsers(string $startDate, int $limit = 10): array
{
return $this->select('COALESCE(NULLIF(TRIM(CONCAT(users.first_name, " ", users.last_name)), ""), users.email, "Guest") AS actor, COUNT(activity_logs.id) AS count')
->join('users', 'users.id = activity_logs.activity_user_id', 'left')
->where('activity_logs.activity_at >=', $startDate)
->groupBy('activity_logs.activity_user_id')
->orderBy('count', 'DESC')
->findAll($limit);
}
protected function getUniqueIPs(string $startDate, int $limit = 10): array
{
return $this->select('ip, COUNT(*) AS count')
->where('activity_at >=', $startDate)
->groupBy('ip')
->orderBy('count', 'DESC')
->findAll($limit);
}
public function getCriticalActions(int $limit = 50): array
{
$criticalActions = ['admin_login', 'admin_password_change', 'system_error', 'security_breach'];
return $this->select('activity_logs.*, COALESCE(NULLIF(TRIM(CONCAT(users.first_name, " ", users.last_name)), ""), users.email, "System") AS actor_name, users.email AS actor_email')
->join('users', 'users.id = activity_logs.activity_user_id', 'left')
->whereIn('action', $criticalActions)
->orderBy('activity_at', 'DESC')
->findAll($limit);
}
public function clearAll(): bool
{
return $this->truncate();
}
}