fetchAdminNotificationUsers(); $subjects = $this->notificationSubjectOptions(); $assignedSubjects = []; $tableReady = $this->hasTable('admin_notification_subjects'); if ($tableReady && !empty($admins)) { $adminIds = array_map('intval', array_column($admins, 'id')); $rows = DB::table('admin_notification_subjects') ->select('id', 'admin_id', 'subject') ->whereIn('admin_id', $adminIds) ->get(); foreach ($rows as $row) { $adminId = (int) ($row->admin_id ?? 0); $subject = (string) ($row->subject ?? ''); if ($adminId <= 0 || $subject === '') { continue; } $assignedSubjects[$adminId] ??= []; $assignedSubjects[$adminId][$subject] = true; } } return [ 'admins' => $admins, 'subjects' => $subjects, 'assigned_subjects' => $assignedSubjects, 'table_ready' => $tableReady, ]; } /** * Input shape (API): * [ * 'subjects' => [ * 12 => ['finance' => true, 'attendance' => true], * 15 => ['general', 'events'] * ] * ] */ public function saveSubjects(array $posted): array { if (!$this->hasTable('admin_notification_subjects')) { return [ 'ok' => false, 'message' => 'Notification subject storage is missing. Run migrations first.', 'updated' => 0, ]; } $subjectsMap = $this->notificationSubjectOptions(); $allowed = array_keys($subjectsMap); $admins = $this->fetchAdminNotificationUsers(); $adminIds = array_map('intval', array_column($admins, 'id')); if (empty($adminIds)) { return [ 'ok' => true, 'message' => 'No admins found to update.', 'updated' => 0, ]; } $existing = DB::table('admin_notification_subjects') ->select('id', 'admin_id', 'subject') ->whereIn('admin_id', $adminIds) ->get(); $existingMap = []; foreach ($existing as $row) { $adminId = (int) ($row->admin_id ?? 0); $subject = (string) ($row->subject ?? ''); if ($adminId <= 0 || $subject === '') { continue; } $existingMap[$adminId] ??= []; $existingMap[$adminId][$subject] = (int) ($row->id ?? 0); } $updates = 0; DB::transaction(function () use ($posted, $adminIds, $allowed, $existingMap, &$updates) { foreach ($adminIds as $adminId) { $subjectRaw = $posted[$adminId] ?? []; $selected = []; if (is_array($subjectRaw)) { foreach ($subjectRaw as $key => $value) { $candidate = is_string($key) ? $key : $value; if (!is_string($candidate)) { continue; } $candidate = trim($candidate); if ($candidate !== '') { $selected[] = $candidate; } } } $selected = array_values(array_unique(array_filter( $selected, fn ($value) => in_array($value, $allowed, true) ))); $current = array_keys($existingMap[$adminId] ?? []); $toDelete = array_values(array_diff($current, $selected)); $toInsert = array_values(array_diff($selected, $current)); if (!empty($toDelete)) { DB::table('admin_notification_subjects') ->where('admin_id', $adminId) ->whereIn('subject', $toDelete) ->delete(); $updates += count($toDelete); } if (!empty($toInsert)) { $batch = []; foreach ($toInsert as $subject) { $batch[] = [ 'admin_id' => $adminId, 'subject' => $subject, ]; } DB::table('admin_notification_subjects')->insert($batch); $updates += count($toInsert); } } }); return [ 'ok' => true, 'message' => $updates > 0 ? 'Notification subjects updated.' : 'No changes were made.', 'updated' => $updates, ]; } public function printRecipientsPayload(): array { $admins = $this->fetchAdminNotificationUsers(); $tableReady = $this->hasTable('admin_notification_subjects'); $assigned = []; if ($tableReady && !empty($admins)) { $adminIds = array_map('intval', array_column($admins, 'id')); $rows = DB::table('admin_notification_subjects') ->select('admin_id') ->where('subject', 'print_requests') ->whereIn('admin_id', $adminIds) ->get(); foreach ($rows as $row) { $adminId = (int) ($row->admin_id ?? 0); if ($adminId > 0) { $assigned[$adminId] = true; } } } return [ 'admins' => $admins, 'assigned' => $assigned, 'table_ready' => $tableReady, ]; } /** * Input shape (API): * ['notify' => [12 => true, 15 => true]] */ public function savePrintRecipients(array $postedNotify): array { if (!$this->hasTable('admin_notification_subjects')) { return [ 'ok' => false, 'message' => 'Notification subject storage is missing. Run migrations first.', 'updated' => 0, ]; } $admins = $this->fetchAdminNotificationUsers(); $adminIds = array_map('intval', array_column($admins, 'id')); if (empty($adminIds)) { return [ 'ok' => true, 'message' => 'No admins found to update.', 'updated' => 0, ]; } $selected = []; foreach ((array) $postedNotify as $key => $value) { $adminId = (int) $key; if ($adminId <= 0) continue; if (!in_array($adminId, $adminIds, true)) continue; $selected[] = $adminId; } $selected = array_values(array_unique($selected)); $existingRows = DB::table('admin_notification_subjects') ->select('admin_id') ->where('subject', 'print_requests') ->whereIn('admin_id', $adminIds) ->get(); $current = array_values(array_unique(array_map( fn ($row) => (int) ($row->admin_id ?? 0), $existingRows->all() ))); $toDelete = array_values(array_diff($current, $selected)); $toInsert = array_values(array_diff($selected, $current)); DB::transaction(function () use ($toDelete, $toInsert) { if (!empty($toDelete)) { DB::table('admin_notification_subjects') ->where('subject', 'print_requests') ->whereIn('admin_id', $toDelete) ->delete(); } if (!empty($toInsert)) { $batch = []; foreach ($toInsert as $adminId) { $batch[] = [ 'admin_id' => $adminId, 'subject' => 'print_requests', ]; } DB::table('admin_notification_subjects')->insert($batch); } }); $changes = count($toDelete) + count($toInsert); return [ 'ok' => true, 'message' => $changes > 0 ? 'Print notification recipients updated.' : 'No changes were made.', 'updated' => $changes, 'selected_admin_ids' => $selected, ]; } public function notificationSubjectOptions(): array { return [ 'academics' => 'Academics', 'attendance' => 'Attendance', 'events' => 'Events', 'finance' => 'Finance', 'general' => 'General', 'print_requests' => 'Print Requests', ]; } private function getAdminNotificationExcludedRoles(): array { return [ 'parent', 'student', 'guest', 'teacher', 'assistant teacher', 'teacher assistant', 'teacher_assistant', 'assistant_teacher', 'ta', 'authorized_user', ]; } public function fetchAdminNotificationUsers(): array { $excluded = array_map( fn ($value) => strtolower(trim((string) $value)), $this->getAdminNotificationExcludedRoles() ); // Safer than raw NOT IN string: use bindings + lower(name) $rows = DB::table('users as u') ->select('u.id', 'u.firstname', 'u.lastname', 'u.email') ->join('user_roles as ur', 'ur.user_id', '=', 'u.id') ->join('roles as r', 'r.id', '=', 'ur.role_id') ->whereNull('ur.deleted_at') ->whereNotNull('r.name') ->whereNotIn(DB::raw('LOWER(r.name)'), $excluded) ->groupBy('u.id', 'u.firstname', 'u.lastname', 'u.email') ->orderBy('u.lastname') ->orderBy('u.firstname') ->get(); return $rows->map(function ($row) { return [ 'id' => (int) $row->id, 'firstname' => (string) ($row->firstname ?? ''), 'lastname' => (string) ($row->lastname ?? ''), 'email' => (string) ($row->email ?? ''), 'full_name' => trim((string)($row->firstname ?? '') . ' ' . (string)($row->lastname ?? '')), ]; })->all(); } private function hasTable(string $table): bool { try { return DB::getSchemaBuilder()->hasTable($table); } catch (Throwable) { return false; } } }