normalizeFilter($schoolYear); $semester = $this->normalizeFilter($semester); $builder = DB::table('parents as sp'); $builder->select([ 'sp.id AS parents_row_id', 'sp.firstparent_id AS firstparent_id', 'sp.secondparent_firstname AS sp_firstname', 'sp.secondparent_lastname AS sp_lastname', 'sp.secondparent_email AS sp_email', 'sp.secondparent_phone AS sp_phone', 'sp.school_year AS sp_school_year', 'sp.semester AS sp_semester', 'u.id AS u_id', 'u.firstname AS u_firstname', 'u.lastname AS u_lastname', 'u.email AS u_email', 'u.cellphone AS u_phone', 'u.school_year AS u_school_year', 'u.semester AS u_semester', ]); $builder->leftJoin('users as u', 'u.id', '=', 'sp.firstparent_id'); if ($schoolYear !== '') { $builder->where('sp.school_year', $schoolYear); } if ($semester !== '') { $builder->where('sp.semester', $semester); } $rows = $builder->get()->all(); $contacts = []; foreach ($rows as $row) { $r = (array) $row; if (! empty($r['u_id'])) { $contacts[] = [ 'firstname' => (string) ($r['u_firstname'] ?? ''), 'lastname' => (string) ($r['u_lastname'] ?? ''), 'email' => (string) ($r['u_email'] ?? ''), 'phone' => (string) ($r['u_phone'] ?? ''), 'type' => 'Primary Parent', ]; } $hasSecond = ! empty($r['sp_firstname']) || ! empty($r['sp_lastname']) || ! empty($r['sp_email']) || ! empty($r['sp_phone']); if ($hasSecond) { $contacts[] = [ 'firstname' => (string) ($r['sp_firstname'] ?? ''), 'lastname' => (string) ($r['sp_lastname'] ?? ''), 'email' => (string) ($r['sp_email'] ?? ''), 'phone' => (string) ($r['sp_phone'] ?? ''), 'type' => 'Second Parent', ]; } } $contacts = array_values(array_filter($contacts, function ($c) { $name = trim(($c['firstname'] ?? '').($c['lastname'] ?? '')); $contact = trim(($c['phone'] ?? '').($c['email'] ?? '')); return $name !== '' || $contact !== ''; })); usort($contacts, function ($a, $b) { $la = strtolower($a['lastname'] ?? ''); $lb = strtolower($b['lastname'] ?? ''); if ($la !== $lb) { return $la <=> $lb; } $fa = strtolower($a['firstname'] ?? ''); $fb = strtolower($b['firstname'] ?? ''); if ($fa !== $fb) { return $fa <=> $fb; } $ta = ($a['type'] ?? '') === 'Primary Parent' ? 0 : 1; $tb = ($b['type'] ?? '') === 'Primary Parent' ? 0 : 1; return $ta <=> $tb; }); return $contacts; } public function listParentContactsByClass(?string $schoolYear, ?string $semester): array { $schoolYear = $this->normalizeFilter($schoolYear); $semester = $this->normalizeFilter($semester); $sections = DB::table('student_class as sc') ->select('sc.class_section_id', DB::raw('MAX(sc.school_year) AS school_year')) ->when($schoolYear !== '', fn ($q) => $q->where('sc.school_year', $schoolYear)) ->groupBy('sc.class_section_id') ->orderBy('sc.class_section_id', 'ASC') ->get() ->all(); $sectionIds = array_values(array_filter(array_unique(array_map( fn ($r) => (int) ($r->class_section_id ?? 0), $sections )))); $namesById = []; if (! empty($sectionIds)) { $nameRows = DB::table('classSection') ->select('class_section_id', 'class_section_name') ->whereIn('class_section_id', $sectionIds) ->get() ->all(); foreach ($nameRows as $row) { $namesById[(int) $row->class_section_id] = (string) ($row->class_section_name ?? ''); } } $classSections = []; foreach ($sections as $s) { $sid = (int) ($s->class_section_id ?? 0); if (! $sid) { continue; } $classSections[$sid] = [ 'class_section_id' => $sid, 'class_section_name' => $namesById[$sid] ?? ('Section '.$sid), 'semester' => $semester, 'school_year' => (string) ($s->school_year ?? ''), 'description' => '', 'main_teachers' => [], 'teacher_assistants' => [], 'parents' => [], ]; } $rows = DB::table('student_class as sc') ->select([ 'sc.class_section_id', 'u.id as primary_id', 'u.firstname as primary_firstname', 'u.lastname as primary_lastname', 'u.email as primary_email', 'u.cellphone as primary_phone', 'sp.id as second_id', 'sp.secondparent_firstname as second_firstname', 'sp.secondparent_lastname as second_lastname', 'sp.secondparent_email as second_email', 'sp.secondparent_phone as second_phone', ]) ->join('students as s', 's.id', '=', 'sc.student_id') ->join('users as u', 'u.id', '=', 's.parent_id') ->leftJoin('parents as sp', function ($join) { $join->on('sp.firstparent_id', '=', 'u.id') ->on('sp.school_year', '=', 'sc.school_year'); }) ->when($schoolYear !== '', fn ($q) => $q->where('sc.school_year', $schoolYear)) ->orderBy('sc.class_section_id', 'ASC') ->orderBy('u.lastname', 'ASC') ->orderBy('u.firstname', 'ASC') ->get() ->all(); $seen = []; foreach ($rows as $row) { $r = (array) $row; $sid = (int) ($r['class_section_id'] ?? 0); $pid = (int) ($r['primary_id'] ?? 0); if (! $sid || ! $pid || ! isset($classSections[$sid])) { continue; } $key = $sid.':'.$pid; if (isset($seen[$key])) { continue; } $seen[$key] = true; $classSections[$sid]['parents'][] = [ 'class_section_id' => $sid, 'primary_id' => $pid, 'primary_name' => trim(($r['primary_lastname'] ?? '').', '.($r['primary_firstname'] ?? '')), 'primary_phone' => (string) ($r['primary_phone'] ?? ''), 'primary_email' => (string) ($r['primary_email'] ?? ''), 'second_id' => (int) ($r['second_id'] ?? 0), 'second_name' => trim(($r['second_lastname'] ?? '').', '.($r['second_firstname'] ?? '')), 'second_phone' => (string) ($r['second_phone'] ?? ''), 'second_email' => (string) ($r['second_email'] ?? ''), 'primary_member' => null, 'second_member' => null, ]; } try { $sectionIds = array_keys($classSections); $membershipMap = WhatsappGroupMembership::getBySectionsAndTerm($sectionIds, $schoolYear, $semester); foreach ($classSections as $sid => &$section) { foreach ($section['parents'] as &$parent) { $key = sprintf('%d:primary:%d', (int) $parent['class_section_id'], (int) $parent['primary_id']); if (isset($membershipMap[$key])) { $parent['primary_member'] = (int) ($membershipMap[$key]['is_member'] ?? 0); } $secondId = (int) ($parent['second_id'] ?? 0); if ($secondId > 0) { $key2 = sprintf('%d:second:%d', (int) $parent['class_section_id'], $secondId); if (isset($membershipMap[$key2])) { $parent['second_member'] = (int) ($membershipMap[$key2]['is_member'] ?? 0); } } } unset($parent); } unset($section); } catch (\Throwable $e) { Log::warning('WhatsApp membership lookup failed: '.$e->getMessage()); } try { $teachers = DB::table('teacher_class as tc') ->select('tc.class_section_id', 'tc.position', 'u.firstname', 'u.lastname') ->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id') ->when($schoolYear !== '', fn ($q) => $q->where('tc.school_year', $schoolYear)) ->get() ->all(); foreach ($teachers as $row) { $r = (array) $row; $sid = (int) ($r['class_section_id'] ?? 0); if (! isset($classSections[$sid])) { continue; } $name = trim(($r['lastname'] ?? '').', '.($r['firstname'] ?? '')); $pos = strtolower((string) ($r['position'] ?? 'main')); if ($pos === 'ta' || $pos === 'assistant') { $classSections[$sid]['teacher_assistants'][] = $name; } else { $classSections[$sid]['main_teachers'][] = $name; } } } catch (\Throwable $e) { Log::warning('WhatsApp teacher list lookup failed: '.$e->getMessage()); } foreach ($classSections as &$section) { $section['main_teachers'] = array_values(array_filter($section['main_teachers'] ?? [])); $section['teacher_assistants'] = array_values(array_filter($section['teacher_assistants'] ?? [])); $section['parents'] = array_values($section['parents'] ?? []); } unset($section); return array_values($classSections); } private function normalizeFilter(?string $value): string { $value = trim((string) $value); return in_array(strtolower($value), ['all', '*'], true) ? '' : $value; } }