getConfigValue('semester') ?? ''); } public function getCurrentSchoolYear(): string { return (string) ($this->getConfigValue('school_year') ?? ''); } protected function getConfigValue(string $key): mixed { return $this->configurationModel ->newQuery() ->where('config_key', $key) ->value('config_value'); } public function getAssignmentsOverview(?string $schoolYear = null, ?string $semester = null): array { $currentSemester = $this->getCurrentSemester(); $currentSchoolYear = $this->getCurrentSchoolYear(); $selectedSemester = (string) ($semester ?? $currentSemester); $selectedYear = (string) ($schoolYear ?? $currentSchoolYear); $teacherClasses = $this->loadTeacherClasses($selectedYear); $studentClasses = $this->loadStudentClasses($selectedYear); $teacherBySection = $teacherClasses->groupBy('class_section_id'); $studentBySection = $studentClasses->groupBy('class_section_id'); $allSectionIds = $teacherBySection->keys() ->merge($studentBySection->keys()) ->unique() ->filter() ->values(); $sectionNames = $this->getSectionNamesMap($allSectionIds->all()); $classSections = $allSectionIds->map(function ($sectionId) use ( $teacherBySection, $studentBySection, $sectionNames, $currentSemester, $currentSchoolYear ) { $teacherRows = $teacherBySection->get($sectionId, collect()); $studentRows = $studentBySection->get($sectionId, collect()); if ($teacherRows->isEmpty() && $studentRows->isEmpty()) { return null; } $mainTeachers = $teacherRows ->where('position', 'main') ->pluck('teacher_full_name') ->filter() ->unique() ->values(); $teacherAssistants = $teacherRows ->where('position', 'ta') ->pluck('teacher_full_name') ->filter() ->unique() ->values(); $semesterMeta = $this->firstNonEmpty([ $teacherRows->pluck('semester')->first(fn ($v) => filled($v)), $studentRows->pluck('semester')->first(fn ($v) => filled($v)), $currentSemester, ]); $schoolYearMeta = $this->firstNonEmpty([ $teacherRows->pluck('school_year')->first(fn ($v) => filled($v)), $studentRows->pluck('school_year')->first(fn ($v) => filled($v)), $currentSchoolYear, ]); $descriptionMeta = $this->firstNonEmpty([ $teacherRows->pluck('description')->first(fn ($v) => filled($v)), $studentRows->pluck('description')->first(fn ($v) => filled($v)), '', ]); $students = $studentRows ->filter(fn ($row) => !empty($row->student)) ->map(function ($row) { $student = $row->student; return [ 'id' => (int) $student->id, 'firstname' => (string) ($student->firstname ?? ''), 'lastname' => (string) ($student->lastname ?? ''), 'age' => $student->age, 'gender' => (string) ($student->gender ?? ''), 'registration_grade' => (string) ($student->registration_grade ?? ''), 'photo_consent' => (bool) ($student->photo_consent ?? false), 'tuition_paid' => (bool) ($student->tuition_paid ?? false), 'school_id' => (string) ($student->school_id ?? ''), ]; }) ->values(); return [ 'class_section_id' => (int) $sectionId, 'class_section_name' => (string) ($sectionNames[$sectionId] ?? ''), 'main_teachers' => $mainTeachers->all(), 'teacher_assistants' => $teacherAssistants->all(), 'students' => $students->all(), 'semester' => (string) $semesterMeta, 'school_year' => (string) $schoolYearMeta, 'description' => (string) $descriptionMeta, ]; }) ->filter() ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) ->values() ->all(); return [ 'classSections' => $classSections, 'schoolYears' => $this->getSchoolYears($currentSchoolYear), 'schoolYear' => $selectedYear, 'selectedYear' => $selectedYear, 'selectedSemester' => $selectedSemester, 'semester' => $currentSemester, 'current_school_year' => $currentSchoolYear, ]; } public function storeAssignment(array $data, int|string|null $updatedBy = null): StudentClass { return $this->studentClassModel->newQuery()->updateOrCreate( [ 'student_id' => $data['student_id'], 'class_section_id' => $data['class_section_id'], 'semester' => $data['semester'], 'school_year' => $data['school_year'], ], [ 'description' => $data['description'] ?? null, 'updated_by' => $updatedBy, ] ); } public function getClassAssignmentData(): array { $currentSemester = $this->getCurrentSemester(); $currentSchoolYear = $this->getCurrentSchoolYear(); $teacherClasses = $this->loadTeacherClasses(); $studentClasses = $this->loadStudentClasses(); $teacherBySection = $teacherClasses->groupBy('class_section_id'); $studentBySection = $studentClasses->groupBy('class_section_id'); $allSectionIds = $teacherBySection->keys() ->merge($studentBySection->keys()) ->unique() ->filter() ->values(); $sectionNames = $this->getSectionNamesMap($allSectionIds->all()); $classSections = $allSectionIds->map(function ($sectionId) use ( $teacherBySection, $studentBySection, $sectionNames, $currentSemester, $currentSchoolYear ) { $teacherRows = $teacherBySection->get($sectionId, collect()); $studentRows = $studentBySection->get($sectionId, collect()); if ($teacherRows->isEmpty() && $studentRows->isEmpty()) { return null; } $mainTeachers = $teacherRows ->where('position', 'main') ->pluck('teacher_full_name') ->filter() ->unique() ->values(); $teacherAssistants = $teacherRows ->where('position', 'ta') ->pluck('teacher_full_name') ->filter() ->unique() ->values(); $semesterMeta = $this->firstNonEmpty([ $teacherRows->pluck('semester')->first(fn ($v) => filled($v)), $studentRows->pluck('semester')->first(fn ($v) => filled($v)), $currentSemester, ]); $schoolYearMeta = $this->firstNonEmpty([ $teacherRows->pluck('school_year')->first(fn ($v) => filled($v)), $studentRows->pluck('school_year')->first(fn ($v) => filled($v)), $currentSchoolYear, ]); $descriptionMeta = $this->firstNonEmpty([ $teacherRows->pluck('description')->first(fn ($v) => filled($v)), $studentRows->pluck('description')->first(fn ($v) => filled($v)), '', ]); $students = $studentRows ->filter(fn ($row) => !empty($row->student)) ->map(function ($row) { $student = $row->student; return [ 'id' => (int) $student->id, 'firstname' => (string) ($student->firstname ?? ''), 'lastname' => (string) ($student->lastname ?? ''), 'age' => $student->age, 'gender' => (string) ($student->gender ?? ''), 'registration_grade' => (string) ($student->registration_grade ?? ''), 'photo_consent' => (bool) ($student->photo_consent ?? false), 'tuition_paid' => (bool) ($student->tuition_paid ?? false), 'school_id' => (string) ($student->school_id ?? ''), ]; }) ->values(); return [ 'class_section_id' => (int) $sectionId, 'class_section_name' => (string) ($sectionNames[$sectionId] ?? ''), 'main_teachers' => $mainTeachers->all(), 'teacher_assistants' => $teacherAssistants->all(), 'students' => $students->all(), 'semester' => (string) $semesterMeta, 'school_year' => (string) $schoolYearMeta, 'description' => (string) $descriptionMeta, ]; }) ->filter() ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) ->values() ->all(); return [ 'classSections' => $classSections, 'semester' => $currentSemester, 'school_year' => $currentSchoolYear, ]; } protected function loadTeacherClasses(?string $schoolYear = null): Collection { return $this->teacherClassModel ->newQuery() ->with([ 'teacher:id,firstname,lastname', ]) ->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear)) ->get() ->map(function ($row) { $row->teacher_full_name = trim( ((string) optional($row->teacher)->firstname) . ' ' . ((string) optional($row->teacher)->lastname) ); return $row; }); } protected function loadStudentClasses(?string $schoolYear = null): Collection { return $this->studentClassModel ->newQuery() ->with([ 'student:id,firstname,lastname,age,gender,registration_grade,photo_consent,tuition_paid,school_id,is_active', ]) ->whereHas('student', fn ($q) => $q->where('is_active', 1)) ->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear)) ->get(); } protected function getSectionNamesMap(array $sectionIds): array { if (empty($sectionIds)) { return []; } return $this->classSectionModel ->newQuery() ->whereIn('id', $sectionIds) ->get() ->mapWithKeys(function ($section) { $name = $section->section_name ?? $section->name ?? ''; return [(int) $section->id => (string) $name]; }) ->all(); } protected function getSchoolYears(string $fallbackYear = ''): array { $years = DB::table('teacher_class') ->selectRaw('DISTINCT school_year') ->whereNotNull('school_year') ->orderByDesc('school_year') ->pluck('school_year') ->map(fn ($year) => (string) $year) ->filter() ->values() ->all(); if (empty($years) && $fallbackYear !== '') { $years[] = $fallbackYear; } return $years; } protected function firstNonEmpty(array $values): mixed { foreach ($values as $value) { if (filled($value)) { return $value; } } return null; } }