'required|integer', 'class_section_id' => 'required|integer', 'semester' => 'required|string|max_length[255]', 'school_year' => 'required|string|max_length[9]', 'position' => 'required|in_list[main,ta]', ]; protected $validationMessages = [ 'teacher_id' => [ 'required' => 'The teacher ID is required.', 'integer' => 'The teacher ID must be an integer.', ], 'class_section_id' => [ 'required' => 'The class section ID is required.', 'integer' => 'The class section ID must be an integer.', ], 'semester' => [ 'required' => 'The semester is required.', ], 'school_year' => [ 'required' => 'The school year is required.', 'max_length' => 'The school year cannot exceed 9 characters.', ], 'position' => [ 'required' => 'The position (main or ta) is required.', 'in_list' => 'The position must be either "main" or "ta".', ], ]; public function getClassSectionIdByUserId($user_id) { $result = $this->where('teacher_id', $user_id)->first(); if (!$result) { log_message('error', "No class section found for user ID: $user_id"); return null; } return $result; } public function getClassAssignmentsByUserId(int $userId, ?string $schoolYear = null, ?string $semester = null): array { $builder = $this->db->table('teacher_class tc') ->select([ 'cs.class_section_name', 'cs.id AS class_section_pk', 'cs.class_section_id', 'tc.teacher_id', 'tc.position', 'tc.school_year', 'tc.semester', ]) ->join('classSection cs', 'tc.class_section_id = cs.class_section_id', 'inner') ->where('tc.teacher_id', $userId) ->where('tc.class_section_id IS NOT NULL', null, false) ->orderBy('cs.class_section_name', 'ASC'); if ($schoolYear) $builder->where('tc.school_year', $schoolYear); if ($semester) $builder->where('tc.semester', $semester); $rows = $builder->get()->getResultArray(); $out = []; foreach ($rows as $r) { $out[] = [ 'class_section_pk' => (int)$r['class_section_pk'], 'class_section_id' => (int)$r['class_section_id'], 'class_section_name' => $r['class_section_name'], 'teacher_id' => (int)$r['teacher_id'], 'teacher_role' => ucfirst($r['position']), // "Main" or "Ta" 'school_year' => $r['school_year'], 'semester' => $r['semester'], ]; } return $out; } public function getClassSectionsByTeacherId($teacherId) { $builder = $this->db->table('teacher_class') ->select('classSection.class_section_name, teacher_class.teacher_id, teacher_class.position') ->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id') ->where('teacher_class.teacher_id', $teacherId) ->where('teacher_class.class_section_id IS NOT NULL', null, false); $result = $builder->get()->getResultArray(); if ($result) { $sectionNames = array_map(fn($row) => $row['class_section_name'], $result); $position = $result[0]['position'] ?? ''; return [ 'role' => ucfirst($position), 'class_section_name' => implode(', ', $sectionNames) ]; } else { return ['role' => '', 'class_section_name' => 'No class assigned']; } } public function getClassByTeacherId($teacherId) { $builder = $this->db->table('teacher_class') ->select('classSection.class_section_id, classSection.class_section_name') ->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id') ->where('teacher_class.teacher_id', $teacherId) ->where('teacher_class.class_section_id IS NOT NULL', null, false); return $builder->get()->getResultArray() ?? []; } public function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear) { $db = \Config\Database::connect(); $section = $db->table('classSection') ->select('class_section_id') ->where('class_section_name', $classSectionName) ->get() ->getRowArray(); if (!$section) return null; $result = $db->table('teacher_class') ->select('teacher_id') ->where([ 'class_section_id' => $section['class_section_id'], 'semester' => $semester, 'school_year' => $schoolYear, 'position' => 'main' ]) ->get() ->getRowArray(); return $result['teacher_id'] ?? null; } public function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array { $db = \Config\Database::connect(); // Adjust table/column names if yours differ (e.g., cs.id vs cs.class_section_id) $row = $db->table('classSection cs') ->select('u.id AS teacher_id, u.firstname, u.lastname') ->join('teacher_class tc', 'tc.class_section_id = cs.id', 'inner') ->join('users u', 'u.id = tc.teacher_id', 'inner') ->where('cs.class_section_name', $classSectionName) ->where('tc.semester', $semester) ->where('tc.school_year', $schoolYear) ->where('tc.position', 'main') ->get() ->getRowArray(); return $row ?: null; // ['teacher_id' => ..., 'firstname' => ..., 'lastname' => ...] or null } /** * Return teacher + TAs for a class section in the current term. * Output: * [ * 'teacher_id' => int|null, * 'teacher_name' => string, // 'N/A' if none found * 'tas' => [ ['id'=>int,'name'=>string], ... ], * ] */ public function getTeacherTABySection(int|string $classSectionId, string $semester, string $schoolYear): array { $db = \Config\Database::connect(); // We can read everything straight from teacher_class; no need to join classSection. // The previous version failed because it filtered on cs.class_section_id even though // the PK is cs.id. We avoid that by filtering on tc.class_section_id directly. $rows = $db->table('teacher_class tc') ->select('u.id AS user_id, u.firstname, u.lastname, tc.position') ->join('users u', 'u.id = tc.teacher_id', 'inner') ->where('tc.class_section_id', $classSectionId) ->where('tc.semester', $semester) ->where('tc.school_year', $schoolYear) ->get() ->getResultArray(); $teacherId = null; $teacherName = 'N/A'; $tas = []; $firstSeen = null; // fallback if no explicit "main"/"teacher" foreach ($rows as $r) { $full = trim(($r['firstname'] ?? '') . ' ' . ($r['lastname'] ?? '')); if ($full === '') continue; if ($firstSeen === null) { $firstSeen = ['id' => (int)$r['user_id'], 'name' => $full]; } $pos = strtolower((string)($r['position'] ?? '')); // Treat either 'main' or legacy 'teacher' as the main teacher role. if (in_array($pos, ['main', 'teacher'], true)) { // Keep the first "main/teacher" as the teacher, ignore additional conflicts. if ($teacherId === null) { $teacherId = (int)$r['user_id']; $teacherName = $full; } continue; } // Treat 'ta', 'assistant', 'teacher_assistant' (and similar) as TA roles. if (in_array($pos, ['ta', 'assistant', 'teacher_assistant'], true)) { $tas[] = ['id' => (int)$r['user_id'], 'name' => $full]; continue; } // Unknown/missing position: don't discard; we’ll use firstSeen as a fallback later. } // Fallback: if no explicit main teacher found, use the first record seen. if ($teacherId === null && $firstSeen !== null) { $teacherId = $firstSeen['id']; $teacherName = $firstSeen['name']; } return [ 'teacher_id' => $teacherId, 'teacher_name' => $teacherName, 'tas' => $tas, ]; } public function assignTeacherToClass($teacherId, $classSectionId, $semester, $schoolYear, $position = 'main') { if (!in_array($position, ['main', 'ta'])) { throw new \InvalidArgumentException('Position must be either "main" or "ta"'); } $existing = $this->where([ 'teacher_id' => $teacherId, 'class_section_id' => $classSectionId, 'semester' => $semester, 'school_year' => $schoolYear, 'position' => $position ])->first(); if ($existing) { return $this->update($existing['id'], ['updated_at' => utc_now()]); } return $this->insert([ 'teacher_id' => $teacherId, 'class_section_id' => $classSectionId, 'semester' => $semester, 'school_year' => $schoolYear, 'position' => $position, 'created_at' => utc_now(), 'updated_at' => utc_now() ]); } public function findAll($limit = 0, $offset = 0) { return parent::findAll($limit, $offset); } public function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester) { return $this->select('teacher_class.class_section_id, teacher_class.position, classSection.class_section_name') ->join('classSection', 'classSection.id = teacher_class.class_section_id') ->where('teacher_class.teacher_id', $teacherId) ->where('teacher_class.school_year', $schoolYear) ->where('teacher_class.semester', $semester) ->findAll(); } /** * Get assigned teachers (with names) for ONE section code + term. * Returns rows: class_section_id, teacher_id, position, firstname, lastname */ public function assignedForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array { $key = $sectionCode . '|' . $semester . '|' . $schoolYear; if (isset($this->assignedCache[$key])) { return $this->assignedCache[$key]; } $rows = $this->db->table('teacher_class tc') ->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname') ->join('users u', 'u.id = tc.teacher_id', 'left') ->where('tc.class_section_id', $sectionCode) // SECTION CODE ->where('tc.semester', $semester) ->where('tc.school_year', $schoolYear) ->orderBy("FIELD(tc.position, 'main','ta')", '', false) // main → ta ->orderBy('u.firstname', 'ASC') ->get()->getResultArray(); return $this->assignedCache[$key] = $rows; } /** * Convenience: same as above but keyed by teacher_id. * Returns: [teacher_id] => row */ public function assignedMapForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array { $out = []; foreach ($this->assignedForSectionTerm($sectionCode, $semester, $schoolYear) as $r) { $out[(int)$r['teacher_id']] = $r; } return $out; } /** * Batch helper for a whole term (optionally filtered to some sections). * Returns: [sectionCode] => [rows...] */ public function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array { $cacheKey = ($onlySectionCodes ? implode(',', array_map('intval', $onlySectionCodes)) : '*') . '|' . $semester . '|' . $schoolYear; if (isset($this->assignedBySectionCache[$cacheKey])) { return $this->assignedBySectionCache[$cacheKey]; } $qb = $this->db->table('teacher_class tc') ->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname') ->join('users u', 'u.id = tc.teacher_id', 'left') ->where('tc.semester', $semester) ->where('tc.school_year', $schoolYear) ->orderBy('tc.class_section_id', 'ASC') ->orderBy("FIELD(tc.position, 'main','ta')", '', false) ->orderBy('u.firstname', 'ASC'); if (!empty($onlySectionCodes)) { $qb->whereIn('tc.class_section_id', array_map('intval', $onlySectionCodes)); } $rows = $qb->get()->getResultArray(); $out = []; foreach ($rows as $r) { $code = (int)$r['class_section_id']; $out[$code][] = $r; } return $this->assignedBySectionCache[$cacheKey] = $out; } }