'integer', 'school_id' => 'integer', 'class_section_id' => 'integer', 'is_event_only' => 'boolean', 'updated_by' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /* ============================================================ * Relationships (optional but recommended) * ============================================================ */ public function student(): BelongsTo { return $this->belongsTo(Student::class, 'student_id'); } public function classSection(): BelongsTo { // Your legacy joins on classSection.class_section_id return $this->belongsTo(ClassSection::class, 'class_section_id', 'class_section_id'); } public function school(): BelongsTo { return $this->belongsTo(School::class, 'school_id'); } public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); // change to Admin if needed } /* ============================================================ * Scopes * ============================================================ */ /** * Scope: only students active for classes/attendance (joins students). * Mirrors legacy active(). */ public function scopeActive(Builder $q): Builder { return $q->select('student_class.*') ->join('students', 'students.id', '=', 'student_class.student_id') ->where('students.is_active', 1); } public function scopeForSection(Builder $q, int $classSectionId): Builder { return $q->where('student_class.class_section_id', $classSectionId); } public function scopeForYear(Builder $q, ?string $schoolYear): Builder { if ($schoolYear === null || trim($schoolYear) === '') { return $q; } return $q->where('student_class.school_year', $schoolYear); } public function scopeNonEvent(Builder $q): Builder { return $q->where('student_class.is_event_only', 0); } /* ============================================================ * legacy-compatible methods (rewritten Laravel-style) * ============================================================ */ public static function getClassSectionNameByStudentId(int $studentId): ?string { return DB::table('student_class as sc') ->join('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->where('sc.student_id', $studentId) ->value('cs.class_section_name'); } /** * Get all students in a specific class section (optionally scoped to schoolYear), active students only. * Mirrors getClassStudents() */ public static function getClassStudents(int $classSectionId, ?string $schoolYear = null) { return static::query() ->active() ->forSection($classSectionId) ->forYear($schoolYear) ->get(); } /** * Return class section names for a student in a given school year. * Mirrors getClassSectionsByStudentId() * * @return array|string */ public static function getClassSectionsByStudentId($studentId, string $schoolYear, bool $asArray = false) { $names = DB::table('student_class as sc') ->join('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->where('sc.student_id', $studentId) ->whereNotNull('sc.class_section_id') ->where('sc.school_year', $schoolYear) ->orderBy('cs.class_section_name', 'asc') ->pluck('cs.class_section_name') ->filter(fn ($v) => $v !== null && trim((string) $v) !== '') ->unique() ->values() ->all(); return $asArray ? $names : (! empty($names) ? implode(', ', $names) : ''); } /** * Return class section names for a student in a given school year, with optional event flag. * Mirrors getClassSectionsByStudentIdWithFlags() * * @return array|string */ public static function getClassSectionsByStudentIdWithFlags($studentId, string $schoolYear, bool $asArray = false) { $rows = DB::table('student_class as sc') ->select('cs.class_section_name', 'sc.is_event_only') ->join('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->where('sc.student_id', $studentId) ->whereNotNull('sc.class_section_id') ->where('sc.school_year', $schoolYear) ->orderBy('cs.class_section_name', 'asc') ->get(); $names = []; foreach ($rows as $r) { $name = $r->class_section_name ?? null; if (! $name) { continue; } $isEvent = (int) ($r->is_event_only ?? 0) === 1; $names[] = $isEvent ? ($name.' (Event)') : $name; } $names = array_values(array_unique(array_filter($names))); return $asArray ? $names : (! empty($names) ? implode(', ', $names) : ''); } /** * Return class_section_id values for a student/year. * Mirrors getClassSectionIdsByStudentId() */ public static function getClassSectionIdsByStudentId($studentId, string $schoolYear): array { $ids = DB::table('student_class') ->where('student_id', $studentId) ->whereNotNull('class_section_id') ->where('school_year', $schoolYear) ->pluck('class_section_id') ->map(fn ($v) => (int) $v) ->filter(fn ($v) => $v > 0) ->unique() ->values() ->all(); return $ids; } /** * Mirrors getStudentsByClassSectionIds() * Returns array rows (not models) like legacy. */ public static function getStudentsByClassSectionIds(array $classSectionIds): array { $ids = array_values(array_unique(array_filter(array_map('intval', $classSectionIds)))); if (empty($ids)) { return []; } return DB::table('student_class as sc') ->selectRaw(' students.id as student_id, students.firstname, students.lastname, students.school_id, students.is_new, students.photo_consent, students.age, sc.school_year, sc.class_section_id ') ->join('students', 'students.id', '=', 'sc.student_id') ->whereIn('sc.class_section_id', $ids) ->where('students.is_active', 1) ->get() ->map(fn ($r) => (array) $r) ->all(); } /** * Get the class ID (grade) for a specific student. * Mirrors getStudentGrade() */ public static function getStudentGrade(int $studentId): string { $sc = static::query() ->where('student_id', $studentId) ->where('is_event_only', 0) ->orderByDesc('created_at') ->first(); if (! $sc || ! $sc->class_section_id) { return 'N/A'; } $classId = DB::table('classSection') ->where('class_section_id', $sc->class_section_id) ->value('class_id'); return $classId ?: 'N/A'; } /** * Return true if the student has at least one non-event class assignment for the year. * Mirrors hasNonEventAssignment() */ public static function hasNonEventAssignment(int $studentId, string $schoolYear): bool { return static::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) ->where('is_event_only', 0) ->exists(); } /** * Return student counts per class_section_id, optionally scoped to a school year. * Mirrors getStudentCountsBySection() * * @return array Map of class_section_id => count */ public static function getStudentCountsBySection(?string $schoolYear = null): array { $q = DB::table('student_class as sc') ->selectRaw('sc.class_section_id, COUNT(*) as total') ->join('students', 'students.id', '=', 'sc.student_id') ->where('students.is_active', 1) ->whereNotNull('sc.class_section_id') ->groupBy('sc.class_section_id'); if ($schoolYear !== null && trim($schoolYear) !== '') { $q->where('sc.school_year', $schoolYear); } $rows = $q->get(); $out = []; foreach ($rows as $r) { $cid = $r->class_section_id; if ($cid === null || $cid === '') { continue; } $out[$cid] = (int) ($r->total ?? 0); } return $out; } /* ============================================================ * Optional validation helper * ============================================================ */ public static function rules(bool $updating = false): array { $req = $updating ? 'sometimes' : 'required'; return [ 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], 'school_id' => ['nullable', 'integer', 'min:1'], 'class_section_id' => ['nullable', 'integer'], 'school_year' => [$req, 'string', 'max:20'], 'is_event_only' => ['nullable', 'boolean'], 'description' => ['nullable', 'string', 'max:2000'], 'updated_by' => ['nullable', 'integer'], ]; } }