310 lines
10 KiB
PHP
310 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',
|
|
'semester',
|
|
'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 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<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'],
|
|
];
|
|
}
|
|
} |