0; })); if ($assignmentIds === []) { return []; } $placeholders = implode(',', array_fill(0, count($assignmentIds), '?')); $rows = TaskCertDb::fetchAll( 'SELECT DISTINCT assignment_id FROM task_certificate WHERE assignment_id IN (' . $placeholders . ')', str_repeat('i', count($assignmentIds)), $assignmentIds ); $result = []; foreach ($rows as $row) { $assignmentId = (int) ($row['assignment_id'] ?? 0); if ($assignmentId <= 0) { continue; } $result[] = $assignmentId; } $result = array_values(array_unique($result)); sort($result); return $result; } public function listForUser(int $contactId): array { return TaskCertDb::fetchAll( 'SELECT c.*, d.title AS task_title FROM task_certificate c INNER JOIN task_definition d ON d.id = c.task_id WHERE c.main_contact_id = ? ORDER BY c.issued_at DESC', 'i', [$contactId] ); } public function listForUserTask(int $contactId, int $taskId): array { return TaskCertDb::fetchAll( 'SELECT c.*, a.cycle_key FROM task_certificate c LEFT JOIN task_assignment a ON a.id = c.assignment_id WHERE c.main_contact_id = ? AND c.task_id = ? ORDER BY c.issued_at DESC, c.id DESC', 'ii', [$contactId, $taskId] ); } public function summarizeForUserTasks(int $contactId, array $taskIds): array { $taskIds = array_values(array_unique(array_map('intval', $taskIds))); $taskIds = array_values(array_filter($taskIds, static function (int $taskId): bool { return $taskId > 0; })); if ($taskIds === []) { return []; } $placeholders = implode(',', array_fill(0, count($taskIds), '?')); $sql = 'SELECT c.task_id, COUNT(*) AS total_count, SUM(CASE WHEN c.status = "valid" THEN 1 ELSE 0 END) AS valid_count, MAX(c.issued_at) AS latest_issued_at, ( SELECT c2.valid_until FROM task_certificate c2 WHERE c2.main_contact_id = c.main_contact_id AND c2.task_id = c.task_id ORDER BY c2.issued_at DESC, c2.id DESC LIMIT 1 ) AS latest_valid_until FROM task_certificate c WHERE c.main_contact_id = ? AND c.task_id IN (' . $placeholders . ') GROUP BY c.task_id'; $types = 'i' . str_repeat('i', count($taskIds)); $params = array_merge([$contactId], $taskIds); $rows = TaskCertDb::fetchAll($sql, $types, $params); $summaryByTask = []; foreach ($rows as $row) { $taskId = (int) ($row['task_id'] ?? 0); if ($taskId <= 0) { continue; } $summaryByTask[$taskId] = [ 'task_id' => $taskId, 'total_count' => (int) ($row['total_count'] ?? 0), 'valid_count' => (int) ($row['valid_count'] ?? 0), 'latest_issued_at' => (string) ($row['latest_issued_at'] ?? ''), 'latest_valid_until' => (string) ($row['latest_valid_until'] ?? ''), ]; } return $summaryByTask; } public function deleteByAssignmentIds(array $assignmentIds): int { $assignmentIds = array_values(array_unique(array_map('intval', $assignmentIds))); $assignmentIds = array_values(array_filter($assignmentIds, static function (int $assignmentId): bool { return $assignmentId > 0; })); if ($assignmentIds === []) { return 0; } $placeholders = implode(',', array_fill(0, count($assignmentIds), '?')); $types = str_repeat('i', count($assignmentIds)); return TaskCertDb::execute( 'DELETE FROM task_certificate WHERE assignment_id IN (' . $placeholders . ')', $types, $assignmentIds ); } public function revokeByAssignmentIds(array $assignmentIds, int $revokedBy, string $reason): int { $assignmentIds = array_values(array_unique(array_map('intval', $assignmentIds))); $assignmentIds = array_values(array_filter($assignmentIds, static function (int $assignmentId): bool { return $assignmentId > 0; })); if ($assignmentIds === []) { return 0; } $placeholders = implode(',', array_fill(0, count($assignmentIds), '?')); $types = 'is' . str_repeat('i', count($assignmentIds)); $params = array_merge([$revokedBy, $reason], $assignmentIds); return TaskCertDb::execute( 'UPDATE task_certificate SET status = "revoked", revoked_at = COALESCE(revoked_at, NOW()), revoked_by = ?, revoke_reason = ? WHERE assignment_id IN (' . $placeholders . ') AND status <> "revoked"', $types, $params ); } public function markExpiredByDate(string $now): int { return TaskCertDb::execute( 'UPDATE task_certificate SET status = "expired" WHERE status = "valid" AND valid_until IS NOT NULL AND valid_until < ?', 's', [$now] ); } }