'integer', 'class_section_id' => 'integer', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /* ============================================================ * Constants * ============================================================ */ public const POS_MAIN = 'main'; public const POS_TA = 'ta'; public static function allowedPositions(): array { return [self::POS_MAIN, self::POS_TA]; } /* ============================================================ * Relationships (optional) * ============================================================ */ public function teacher(): BelongsTo { // your teachers are users in most of your joins return $this->belongsTo(User::class, 'teacher_id'); } public function classSection(): BelongsTo { // CI frequently joins on classSection.class_section_id return $this->belongsTo(ClassSection::class, 'class_section_id', 'class_section_id'); } public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); // change to Admin if needed } /* ============================================================ * Scopes * ============================================================ */ public function scopeForTeacher(Builder $q, int $teacherId): Builder { return $q->where('teacher_id', $teacherId); } public function scopeForSchoolYear(Builder $q, string $schoolYear): Builder { return $q->where('school_year', $schoolYear); } public function scopeForSectionCode(Builder $q, int $sectionCode): Builder { return $q->where('class_section_id', $sectionCode); } public function scopeMain(Builder $q): Builder { return $q->where('position', self::POS_MAIN); } public function scopeTa(Builder $q): Builder { return $q->where('position', self::POS_TA); } /* ============================================================ * CI-compatible methods (converted) * ============================================================ */ /** CI: getClassSectionIdByUserId() */ public static function getClassSectionIdByUserId(int $userId): ?self { return static::query()->forTeacher($userId)->first(); } /** * CI: getClassAssignmentsByUserId() * Returns array rows with: * class_section_pk, class_section_id, class_section_name, class_id, class_name, teacher_id, teacher_role, school_year, semester */ public static function getClassAssignmentsByUserId(int $userId, ?string $schoolYear = null, ?string $semester = null): array { $q = DB::table('teacher_class as tc') ->selectRaw(' cs.class_section_name, cs.id AS class_section_pk, cs.class_section_id, c.id AS class_id, c.class_name, tc.teacher_id, tc.position, tc.school_year ') ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id') ->leftJoin('classes as c', 'cs.class_id', '=', 'c.id') ->where('tc.teacher_id', $userId) ->whereNotNull('tc.class_section_id') ->orderBy('cs.class_section_name', 'asc'); if ($schoolYear) { $q->where('tc.school_year', $schoolYear); } $rows = $q->get(); return $rows->map(function ($r) use ($semester) { return [ 'class_section_pk' => isset($r->class_section_pk) ? (int) $r->class_section_pk : null, 'class_section_id' => (int) $r->class_section_id, 'class_section_name' => (string) $r->class_section_name, 'class_id' => isset($r->class_id) ? (int) $r->class_id : null, 'class_name' => $r->class_name ?? null, 'teacher_id' => (int) $r->teacher_id, 'teacher_role' => ucfirst((string) $r->position), // Main / Ta 'school_year' => (string) $r->school_year, 'semester' => $semester, ]; })->all(); } /** * Distinct `class_section_id` values this teacher has ever been assigned to (`teacher_class`), * ignoring school year / semester (covers legacy rows vs `class_progress_reports`). * * @return list */ public static function distinctSectionIdsForTeacher(int $userId): array { return static::query() ->where('teacher_id', $userId) ->whereNotNull('class_section_id') ->where('class_section_id', '>', 0) ->distinct() ->orderBy('class_section_id') ->pluck('class_section_id') ->map(static fn ($id) => (int) $id) ->unique() ->values() ->all(); } /** CI: getClassSectionsByTeacherId() */ public static function getClassSectionsByTeacherId(int $teacherId): array { $rows = DB::table('teacher_class as tc') ->selectRaw('cs.class_section_name, tc.teacher_id, tc.position') ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id') ->where('tc.teacher_id', $teacherId) ->whereNotNull('tc.class_section_id') ->get(); if ($rows->isEmpty()) { return ['role' => '', 'class_section_name' => 'No class assigned']; } $sectionNames = $rows->pluck('class_section_name')->filter()->all(); $position = (string) ($rows[0]->position ?? ''); return [ 'role' => ucfirst($position), 'class_section_name' => implode(', ', $sectionNames), ]; } /** CI: getClassByTeacherId() */ public static function getClassByTeacherId(int $teacherId): array { return DB::table('teacher_class as tc') ->selectRaw('cs.class_section_id, cs.class_section_name') ->join('classSection as cs', 'tc.class_section_id', '=', 'cs.class_section_id') ->where('tc.teacher_id', $teacherId) ->whereNotNull('tc.class_section_id') ->get() ->map(fn ($r) => (array) $r) ->all(); } /** CI: getTeacherIdByClassSection($classSectionName, $semester, $schoolYear) */ public static function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear): ?int { $classSectionName = trim($classSectionName); if (str_contains($classSectionName, ',')) { $parts = array_values(array_filter(array_map('trim', explode(',', $classSectionName)))); if (!empty($parts)) $classSectionName = $parts[0]; } if ($classSectionName === '') return null; $sectionCode = DB::table('classSection') ->where('class_section_name', $classSectionName) ->value('class_section_id'); if (!$sectionCode) return null; $teacherId = DB::table('teacher_class') ->where('class_section_id', $sectionCode) ->where('school_year', $schoolYear) ->where('position', self::POS_MAIN) ->value('teacher_id'); return $teacherId ? (int) $teacherId : null; } /** CI: getMainTeacherBySection($classSectionName, $semester, $schoolYear) */ public static function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array { $row = DB::table('classSection as cs') ->selectRaw('u.id AS teacher_id, u.firstname, u.lastname') // IMPORTANT: your CI code had tc.class_section_id = cs.id in one place (PK join), // but most of your code uses class_section_id code join. // Here we keep the "code join" for consistency: ->join('teacher_class as tc', 'tc.class_section_id', '=', 'cs.class_section_id') ->join('users as u', 'u.id', '=', 'tc.teacher_id') ->where('cs.class_section_name', $classSectionName) ->where('tc.school_year', $schoolYear) ->where('tc.position', self::POS_MAIN) ->first(); return $row ? (array) $row : null; } /** * CI: assignTeacherToClass() * Upsert by (teacher_id, class_section_id, school_year, position) and touch updated_at. */ public static function assignTeacherToClass( int $teacherId, int $sectionCode, string $semester, string $schoolYear, string $position = self::POS_MAIN, ?int $updatedBy = null ): bool { $position = strtolower(trim($position)); if (!in_array($position, self::allowedPositions(), true)) { throw new \InvalidArgumentException('Position must be either "main" or "ta".'); } $updatedBy = $updatedBy ?? (auth()->id() ?? null); static::query()->updateOrCreate( [ 'teacher_id' => $teacherId, 'class_section_id' => $sectionCode, 'school_year' => $schoolYear, 'position' => $position, ], [ 'updated_by' => $updatedBy, 'updated_at' => now(), ] ); return true; } /** * CI: getAssignedClassSections() * NOTE: your CI joins classSection.id = teacher_class.class_section_id (PK join). * If your teacher_class.class_section_id is actually a section CODE, change join to cs.class_section_id. */ public static function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester): array { return DB::table('teacher_class as tc') ->selectRaw('tc.class_section_id, tc.position, cs.class_section_name') ->join('classSection as cs', 'cs.class_section_id', '=', 'tc.class_section_id') ->where('tc.teacher_id', $teacherId) ->where('tc.school_year', $schoolYear) ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END") ->get() ->map(fn ($r) => (array) $r) ->all(); } /** * CI: assignedForSectionTerm() * Returns rows: class_section_id, teacher_id, position, firstname, lastname * * @param int|null $alsoSectionCode When teacher_class rows use the section PK while $sectionCode * is the business code (or the reverse), pass the other id. * @param bool $requireSemester When false, ignore semester (legacy DBs with blank/wrong tc.semester). */ public static function assignedForSectionTerm( int $sectionCode, string $semester, string $schoolYear, ?int $alsoSectionCode = null, bool $requireSemester = true, ): array { $sectionIds = array_values(array_unique(array_filter( [$sectionCode, $alsoSectionCode ?? 0], static fn (int $v): bool => $v > 0, ))); if ($sectionIds === []) { return []; } $q = DB::table('teacher_class as tc') ->selectRaw('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname') ->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id') ->whereIn('tc.class_section_id', $sectionIds) ->where('tc.school_year', $schoolYear); if ($requireSemester) { $sem = trim($semester); if ($sem !== '') { $q->whereRaw('TRIM(tc.semester) = ?', [$sem]); } } return $q->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END") ->orderBy('u.firstname', 'asc') ->get() ->map(fn ($r) => (array) $r) ->all(); } /** CI: assignedMapForSectionTerm() */ public static function assignedMapForSectionTerm( int $sectionCode, string $semester, string $schoolYear, ?int $alsoSectionCode = null, bool $requireSemester = true, ): array { $out = []; foreach (static::assignedForSectionTerm($sectionCode, $semester, $schoolYear, $alsoSectionCode, $requireSemester) as $r) { $out[(int) $r['teacher_id']] = $r; } return $out; } /** * CI: assignedBySectionForTerm() * Returns: [sectionCode] => [rows...] */ public static function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array { $q = DB::table('teacher_class as tc') ->selectRaw('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname') ->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id') ->where('tc.school_year', $schoolYear) ->orderBy('tc.class_section_id', 'asc') ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END") ->orderBy('u.firstname', 'asc'); if (!empty($onlySectionCodes)) { $ids = array_values(array_unique(array_filter(array_map('intval', $onlySectionCodes)))); if (!empty($ids)) $q->whereIn('tc.class_section_id', $ids); } $rows = $q->get()->map(fn ($r) => (array) $r)->all(); $out = []; foreach ($rows as $r) { $code = (int) ($r['class_section_id'] ?? 0); if ($code <= 0) continue; $out[$code][] = $r; } return $out; } /* ============================================================ * Optional validation helper (FormRequest/controller) * ============================================================ */ public static function rules(bool $updating = false): array { $req = $updating ? 'sometimes' : 'required'; return [ 'teacher_id' => [$req, 'integer', 'min:1', 'exists:users,id'], 'class_section_id' => [$req, 'integer', 'min:1'], 'school_year' => [$req, 'string', 'max:9'], 'position' => [$req, 'string', 'in:' . implode(',', self::allowedPositions())], 'updated_by' => ['nullable', 'integer'], ]; } }