0, 'student_ids' => 0, ]; $classStudentsQuery = $this->studentClassModel->query(); if (method_exists($this->studentClassModel, 'scopeActive')) { $classStudentsQuery->active(); } $classStudents = $classStudentsQuery ->where('student_class.school_year', $schoolYear) ->get() ->toArray(); $debug['class_students'] = count($classStudents); if (empty($classStudents)) { $latestTerm = DB::table('student_class') ->select('school_year', 'semester') ->orderByDesc('id') ->first(); if ($latestTerm && !empty($latestTerm->school_year)) { $schoolYear = (string) $latestTerm->school_year; if (!empty($latestTerm->semester)) { $semester = (string) $latestTerm->semester; } $classStudentsQuery = $this->studentClassModel->query(); if (method_exists($this->studentClassModel, 'scopeActive')) { $classStudentsQuery->active(); } $classStudents = $classStudentsQuery ->where('student_class.school_year', $schoolYear) ->get() ->toArray(); $debug['class_students'] = count($classStudents); } } [$studentIds, $studentCodes] = $this->extractStudentIdentifiers($classStudents); if (empty($studentIds) && empty($studentCodes)) { [$studentIds, $studentCodes] = $this->deriveIdentifiersFromAttendanceData($schoolYear); } [$studentIds, $studentCodes] = $this->filterInactiveIdentifiers($studentIds, $studentCodes); $debug['student_ids'] = count($studentIds) + count($studentCodes); if (empty($studentIds) && empty($studentCodes)) { return [ 'school_year' => $schoolYear, 'semester' => $semester, 'student_ids' => [], 'student_codes' => [], 'students' => [], 'student_code_to_id'=> [], 'existing_ids' => [], 'debug' => $debug, ]; } $students = $this->loadActiveStudents($studentIds, $studentCodes); $students = $this->addPlaceholderStudentsForMissingCodes($students, $studentCodes); $students = $this->ensureEntriesForAllIdentifiers($students, $studentIds, $studentCodes); [$studentCodeToId, $existingIds] = $this->buildLookupMaps($students); return [ 'school_year' => $schoolYear, 'semester' => $semester, 'student_ids' => $studentIds, 'student_codes' => $studentCodes, 'students' => $students, 'student_code_to_id' => $studentCodeToId, 'existing_ids' => $existingIds, 'debug' => $debug, ]; } public function ensureAttendanceStudentExists( array &$students, array &$studentCodeToId, array &$existingIds, mixed $rawStudentId ): ?int { if (is_numeric($rawStudentId)) { $sid = (int) $rawStudentId; if ($sid <= 0) { return null; } if (!isset($existingIds[$sid])) { $students[] = [ 'id' => $sid, 'school_id' => (string) $sid, 'firstname' => (string) $sid, 'lastname' => '', 'parent_id' => 0, ]; $existingIds[$sid] = true; } return $sid; } if (is_string($rawStudentId)) { $code = trim($rawStudentId); if ($code === '') { return null; } $sid = $studentCodeToId[$code] ?? null; if ($sid === null) { $sid = abs(crc32($code)) ?: random_int(3000000, 3999999); $studentCodeToId[$code] = $sid; if (!isset($existingIds[$sid])) { $students[] = [ 'id' => $sid, 'school_id' => $code, 'firstname' => $code, 'lastname' => '', 'parent_id' => 0, ]; $existingIds[$sid] = true; } } return $sid; } return null; } protected function extractStudentIdentifiers(array $classStudents): array { $studentIds = []; $studentCodes = []; foreach ($classStudents as $row) { $raw = $row['student_id'] ?? null; if (is_numeric($raw)) { $studentIds[] = (int) $raw; } elseif (is_string($raw) && $raw !== '') { $studentCodes[] = trim($raw); } } $studentIds = array_values(array_unique(array_filter($studentIds, fn ($v) => $v > 0))); $studentCodes = array_values(array_unique(array_filter($studentCodes, fn ($v) => $v !== ''))); return [$studentIds, $studentCodes]; } protected function deriveIdentifiersFromAttendanceData(string $schoolYear): array { $studentIds = []; $studentCodes = []; $attendanceStudents = DB::table($this->attendanceDataModel->getTable()) ->selectRaw('DISTINCT student_id') ->when(!empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear)) ->orderBy('student_id') ->get(); foreach ($attendanceStudents as $r) { $raw = $r->student_id ?? null; if (is_numeric($raw)) { $studentIds[] = (int) $raw; } elseif (is_string($raw) && $raw !== '') { $studentCodes[] = trim($raw); } } $studentIds = array_values(array_unique(array_filter($studentIds, fn ($v) => $v > 0))); $studentCodes = array_values(array_unique(array_filter($studentCodes, fn ($v) => $v !== ''))); return [$studentIds, $studentCodes]; } protected function filterInactiveIdentifiers(array $studentIds, array $studentCodes): array { if (!empty($studentIds)) { $inactiveRows = $this->studentModel->query() ->select('id') ->whereIn('id', $studentIds) ->where('is_active', 0) ->get() ->toArray(); $inactiveIdSet = array_flip(array_map( static fn ($r) => (int) ($r['id'] ?? 0), $inactiveRows )); $studentIds = array_values(array_filter( $studentIds, static fn ($id) => !isset($inactiveIdSet[$id]) )); } if (!empty($studentCodes)) { $inactiveCodeRows = $this->studentModel->query() ->select('school_id') ->whereIn('school_id', $studentCodes) ->where('is_active', 0) ->get() ->toArray(); $inactiveCodeSet = array_flip(array_map( static fn ($r) => (string) ($r['school_id'] ?? ''), $inactiveCodeRows )); $studentCodes = array_values(array_filter( $studentCodes, static fn ($code) => $code !== '' && !isset($inactiveCodeSet[$code]) )); } return [$studentIds, $studentCodes]; } protected function loadActiveStudents(array $studentIds, array $studentCodes): array { $students = []; if (!empty($studentIds)) { $students = $this->studentModel->query() ->whereIn('id', $studentIds) ->where('is_active', 1) ->get() ->toArray(); } if (!empty($studentCodes)) { $byCode = $this->studentModel->query() ->whereIn('school_id', $studentCodes) ->where('is_active', 1) ->get() ->toArray(); $seen = array_flip(array_map(fn ($s) => (int) ($s['id'] ?? 0), $students)); foreach ($byCode as $s) { $sid = (int) ($s['id'] ?? 0); if ($sid > 0 && !isset($seen[$sid])) { $students[] = $s; $seen[$sid] = true; } } } return $students; } protected function addPlaceholderStudentsForMissingCodes(array $students, array $studentCodes): array { $knownCodes = []; foreach ($students as $s) { $code = trim((string) ($s['school_id'] ?? '')); if ($code !== '') { $knownCodes[$code] = true; } } foreach ($studentCodes as $code) { if (!isset($knownCodes[$code])) { $virtualId = abs(crc32($code)) ?: random_int(1000000, 1999999); $students[] = [ 'id' => $virtualId, 'school_id' => $code, 'firstname' => $code, 'lastname' => '', 'parent_id' => 0, 'class_name' => '', ]; } } return $students; } protected function ensureEntriesForAllIdentifiers(array $students, array $studentIds, array $studentCodes): array { [$studentCodeToId, $existingIds] = $this->buildLookupMaps($students); foreach (array_merge($studentIds, $studentCodes) as $sidOrCode) { if (is_numeric($sidOrCode)) { $sid = (int) $sidOrCode; if ($sid > 0 && !isset($existingIds[$sid])) { $students[] = [ 'id' => $sid, 'school_id' => (string) $sid, 'firstname' => (string) $sid, 'lastname' => '', 'parent_id' => 0, 'class_name' => '', ]; $existingIds[$sid] = true; } } else { $code = (string) $sidOrCode; if ($code !== '' && !isset($studentCodeToId[$code])) { $virtualId = abs(crc32($code)) ?: random_int(2000000, 2999999); $students[] = [ 'id' => $virtualId, 'school_id' => $code, 'firstname' => $code, 'lastname' => '', 'parent_id' => 0, 'class_name' => '', ]; $studentCodeToId[$code] = $virtualId; $existingIds[$virtualId] = true; } } } return $students; } protected function buildLookupMaps(array $students): array { $studentCodeToId = []; foreach ($students as $stu) { $code = trim((string) ($stu['student_code'] ?? $stu['school_id'] ?? '')); if ($code !== '') { $studentCodeToId[$code] = (int) ($stu['id'] ?? 0); } } $existingIds = array_flip(array_map( fn ($s) => (int) ($s['id'] ?? 0), $students )); return [$studentCodeToId, $existingIds]; } }