fetchActiveStudentsWithSection($schoolYear) : []; $batches = $this->fetchPlacementBatches($schoolYear); $batchDetails = $this->fetchPlacementBatchDetails($batches, $schoolYear); return [ 'school_year' => $schoolYear, 'students' => $students, 'batches' => $batches, 'batch_details' => $batchDetails, 'placement_test' => $placementTest, 'show_students' => $showStudents, ]; } $sectionName = ClassSection::query()->where('class_section_id', $classSectionId)->value('class_section_name'); $classId = ClassSection::query()->where('class_section_id', $classSectionId)->value('class_id'); $students = Student::getStudentInfoByClassSectionId($classSectionId, null, $schoolYear); $studentIds = array_values(array_filter(array_map( static fn ($row) => (int) ($row['student_id'] ?? 0), $students ), static fn ($id) => $id > 0)); $levels = []; if (! empty($studentIds)) { $rows = PlacementLevel::query() ->whereIn('student_id', $studentIds) ->where('school_year', $schoolYear) ->get(); foreach ($rows as $row) { $levels[(int) $row->student_id] = $row->level ?? null; } } return [ 'class_section_id' => $classSectionId, 'class_section_name' => $sectionName ?? '', 'class_id' => $classId, 'school_year' => $schoolYear, 'students' => $students, 'levels' => $levels, ]; } public function updatePlacementLevel(int $studentId, string $schoolYear, $level, ?int $userId): void { if ($studentId <= 0 || $schoolYear === '') { throw new RuntimeException('Missing student or school year.'); } $level = $level === '' || $level === null ? null : (int) $level; if ($level !== null && ! in_array($level, [1, 2, 3], true)) { throw new RuntimeException('Invalid placement level.'); } $existing = PlacementLevel::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) ->first(); if ($level === null) { if ($existing) { $existing->delete(); } return; } $payload = [ 'student_id' => $studentId, 'school_year' => $schoolYear, 'level' => $level, 'updated_by' => $userId, ]; if ($existing) { $existing->update($payload); } else { $payload['created_by'] = $userId; PlacementLevel::query()->create($payload); } } public function updatePlacementLevels(int $classSectionId, string $schoolYear, array $levels, ?int $userId): void { if ($classSectionId <= 0 || $schoolYear === '') { throw new RuntimeException('Missing class section or school year.'); } $students = Student::getStudentInfoByClassSectionId($classSectionId, null, $schoolYear); $validIds = array_values(array_filter(array_map( static fn ($row) => (int) ($row['student_id'] ?? 0), $students ), static fn ($id) => $id > 0)); $validSet = array_flip($validIds); $existingRows = []; if (! empty($validIds)) { $rows = PlacementLevel::query() ->whereIn('student_id', $validIds) ->where('school_year', $schoolYear) ->get(); foreach ($rows as $row) { $existingRows[(int) $row->student_id] = $row; } } foreach ($levels as $studentIdRaw => $levelRaw) { $studentId = (int) $studentIdRaw; if (! isset($validSet[$studentId])) { continue; } $levelRaw = trim((string) $levelRaw); $level = $levelRaw === '' ? null : (int) $levelRaw; if ($level !== null && ! in_array($level, [1, 2, 3], true)) { continue; } if ($level === null) { if (isset($existingRows[$studentId])) { $existingRows[$studentId]->delete(); } continue; } $payload = [ 'student_id' => $studentId, 'school_year' => $schoolYear, 'level' => $level, 'updated_by' => $userId, ]; if (isset($existingRows[$studentId])) { $existingRows[$studentId]->update($payload); } else { $payload['created_by'] = $userId; PlacementLevel::query()->create($payload); } } } public function createPlacementBatch(string $schoolYear, string $placementTest, array $levels, ?int $userId): int { if ($schoolYear === '' || $placementTest === '') { throw new RuntimeException('Missing placement test or school year.'); } $students = $this->fetchActiveStudentsWithSection($schoolYear); $validIds = array_values(array_filter(array_map( static fn ($row) => (int) ($row['student_id'] ?? 0), $students ), static fn ($id) => $id > 0)); $validSet = array_flip($validIds); $batch = PlacementBatch::query()->create([ 'placement_test' => $placementTest, 'school_year' => $schoolYear, 'created_by' => $userId, 'updated_by' => $userId, ]); $savedCount = 0; foreach ($levels as $studentIdRaw => $levelRaw) { $studentId = (int) $studentIdRaw; if (! isset($validSet[$studentId])) { continue; } $levelRaw = trim((string) $levelRaw); if ($levelRaw === '') { continue; } $score = (int) $levelRaw; if ($score < 0 || $score > 100) { continue; } PlacementScore::query()->create([ 'batch_id' => (int) $batch->id, 'student_id' => $studentId, 'score' => $score, 'created_by' => $userId, 'updated_by' => $userId, ]); $savedCount++; } if ($savedCount === 0) { $batch->delete(); throw new RuntimeException('No scores entered. Batch not saved.'); } return (int) $batch->id; } public function getPlacementBatch(int $batchId): array { $batch = PlacementBatch::query()->find($batchId); if (! $batch) { throw new RuntimeException('Placement batch not found.'); } $schoolYear = (string) ($batch->school_year ?? ''); $students = $this->fetchActiveStudentsWithSection($schoolYear); $scores = $this->fetchPlacementScoresForBatch($batchId); return [ 'batch' => $batch, 'students' => $students, 'scores' => $scores, ]; } public function updatePlacementBatch(int $batchId, string $schoolYear, array $levels, ?int $userId): void { $batch = PlacementBatch::query()->find($batchId); if (! $batch) { throw new RuntimeException('Placement batch not found.'); } $students = $this->fetchActiveStudentsWithSection($schoolYear); $validIds = array_values(array_filter(array_map( static fn ($row) => (int) ($row['student_id'] ?? 0), $students ), static fn ($id) => $id > 0)); $validSet = array_flip($validIds); $existing = $this->fetchPlacementScoresForBatch($batchId); foreach ($levels as $studentIdRaw => $scoreRaw) { $studentId = (int) $studentIdRaw; if (! isset($validSet[$studentId])) { continue; } $scoreRaw = trim((string) $scoreRaw); if ($scoreRaw === '') { if (isset($existing[$studentId])) { PlacementScore::query()->whereKey($existing[$studentId]['id'])->delete(); } continue; } $score = (int) $scoreRaw; if ($score < 0 || $score > 100) { continue; } if (isset($existing[$studentId])) { PlacementScore::query()->whereKey($existing[$studentId]['id'])->update([ 'score' => $score, 'updated_by' => $userId, ]); } else { PlacementScore::query()->create([ 'batch_id' => $batchId, 'student_id' => $studentId, 'score' => $score, 'created_by' => $userId, 'updated_by' => $userId, ]); } } $batch->update([ 'updated_by' => $userId, ]); } private function fetchActiveStudentsWithSection(string $schoolYear): array { return DB::table('students as s') ->select('s.id as student_id', 's.school_id', 's.firstname', 's.lastname', 'sc.class_section_id', 'cs.class_section_name', 'c.class_name') ->leftJoin('student_class as sc', function ($join) use ($schoolYear) { $join->on('sc.student_id', '=', 's.id') ->where('sc.school_year', '=', $schoolYear); }) ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->leftJoin('classes as c', 'c.id', '=', 'cs.class_id') ->where('s.is_active', 1) ->orderBy('s.lastname', 'ASC') ->orderBy('s.firstname', 'ASC') ->get() ->map(fn ($row) => (array) $row) ->all(); } private function fetchPlacementBatches(string $schoolYear): array { return PlacementBatch::query() ->where('school_year', $schoolYear) ->orderBy('created_at', 'DESC') ->get() ->map(fn ($row) => (array) $row) ->all(); } private function fetchPlacementBatchDetails(array $batches, string $schoolYear): array { if (empty($batches)) { return []; } $batchIds = array_values(array_filter(array_map( static fn ($row) => (int) ($row['id'] ?? 0), $batches ), static fn ($id) => $id > 0)); if (empty($batchIds)) { return []; } $rows = DB::table('placement_scores as ps') ->select('ps.batch_id', 'ps.score', 's.school_id', 's.firstname', 's.lastname', 'cs.class_section_name', 'c.class_name') ->join('students as s', 's.id', '=', 'ps.student_id') ->leftJoin('student_class as sc', function ($join) use ($schoolYear) { $join->on('sc.student_id', '=', 's.id') ->where('sc.school_year', '=', $schoolYear); }) ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->leftJoin('classes as c', 'c.id', '=', 'cs.class_id') ->whereIn('ps.batch_id', $batchIds) ->where('s.is_active', 1) ->orderBy('ps.batch_id', 'ASC') ->orderBy('s.lastname', 'ASC') ->orderBy('s.firstname', 'ASC') ->get() ->map(fn ($row) => (array) $row) ->all(); $details = []; foreach ($rows as $row) { $bid = (int) ($row['batch_id'] ?? 0); if ($bid <= 0) { continue; } $details[$bid][] = $row; } return $details; } private function fetchPlacementScoresForBatch(int $batchId): array { $rows = PlacementScore::query() ->where('batch_id', $batchId) ->get() ->map(fn ($row) => (array) $row) ->all(); $scores = []; foreach ($rows as $row) { $scores[(int) $row['student_id']] = $row; } return $scores; } }