Files
alrahma_sunday_school_api/app/Models/StudentClass.php
T
2026-04-23 00:04:35 -04:00

309 lines
10 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\DB;
class StudentClass extends BaseModel
{
protected $table = 'student_class';
protected $fillable = [
'student_id',
'school_id',
'class_section_id',
'school_year',
'is_event_only',
'description',
'updated_by',
'updated_at',
'created_at',
];
public $timestamps = true;
protected $casts = [
'student_id' => '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 CI 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 CI 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);
}
/* ============================================================
* CI-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 CI.
*/
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<int|string,int> 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'],
];
}
}