reconstruction of the project

This commit is contained in:
root
2026-03-08 16:33:24 -04:00
parent 23b7db1107
commit c8de5f7edc
9157 changed files with 77877 additions and 1073823 deletions
+310 -439
View File
@@ -2,12 +2,21 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;
class Student extends BaseModel
{
protected $table = 'students';
protected $primaryKey = 'id'; // Primary key of the students table
/**
* CI: timestamps disabled
*/
public $timestamps = false;
protected $fillable = [
'school_id',
'firstname',
@@ -27,498 +36,360 @@ class Student extends BaseModel
'year_of_registration',
'school_year',
];
public $timestamps = false;
// File: app/Models/Student.php
/**
* Get all students marked as new (is_new = 1).
*
* @return array
protected $casts = [
'school_id' => 'integer',
'age' => 'integer',
'photo_consent' => 'boolean',
'is_new' => 'boolean',
'parent_id' => 'integer',
'tuition_paid' => 'boolean',
'is_active' => 'boolean',
'dob' => 'date',
'registration_date' => 'date',
'year_of_registration' => 'integer',
];
/* ============================================================
* Relationships (optional but useful)
* ============================================================
*/
public function getNewStudents(): array
public function parent(): BelongsTo
{
return $this->where('is_new', 1)
->orderBy('lastname', 'ASC')
->orderBy('firstname', 'ASC')
->findAll();
// Change User model if your parent lives elsewhere
return $this->belongsTo(User::class, 'parent_id');
}
public function getNewStudentsWithParents(): array
public function classAssignments(): HasMany
{
return $this->select([
'students.*',
'u.id AS parent_id',
'u.firstname AS parent_firstname',
'u.lastname AS parent_lastname',
'u.email AS parent_email',
'u.cellphone AS parent_phone',
])
->join('users u', 'u.id = students.parent_id', 'left')
return $this->hasMany(StudentClass::class, 'student_id');
}
public function allergies(): HasMany
{
return $this->hasMany(StudentAllergy::class, 'student_id');
}
public function medicalConditions(): HasMany
{
return $this->hasMany(StudentMedicalCondition::class, 'student_id');
}
/* ============================================================
* Scopes
* ============================================================
*/
public function scopeActive(Builder $q): Builder
{
return $q->where('is_active', 1);
}
public function scopeNew(Builder $q): Builder
{
return $q->where('is_new', 1);
}
public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder
{
if ($schoolYear === null || strtolower(trim($schoolYear)) === 'all' || trim($schoolYear) === '') {
return $q;
}
return $q->where('school_year', $schoolYear);
}
public function scopeOrderByName(Builder $q): Builder
{
return $q->orderBy('lastname', 'asc')->orderBy('firstname', 'asc');
}
/* ============================================================
* CI-compatible methods (converted)
* ============================================================
*/
/** CI: getNewStudents() */
public static function getNewStudents(): array
{
return static::query()
->new()
->orderByName()
->get()
->all();
}
/** CI: getNewStudentsWithParents() */
public static function getNewStudentsWithParents(): array
{
return DB::table('students')
->selectRaw('
students.*,
u.id AS parent_id,
u.firstname AS parent_firstname,
u.lastname AS parent_lastname,
u.email AS parent_email,
u.cellphone AS parent_phone
')
->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
->where('students.is_new', 1)
->orderBy('students.lastname', 'ASC')
->orderBy('students.firstname', 'ASC')
->findAll();
->orderBy('students.lastname', 'asc')
->orderBy('students.firstname', 'asc')
->get()
->map(fn ($r) => (array) $r)
->all();
}
/**
* Fetch only new students (is_new=1) with parent info and a single emergency contact.
* - Parent fields: parent_firstname, parent_lastname, parent_email, parent_phone
* - Emergency fields: emergency_name, emergency_relationship, emergency_phone
* CI: getNewStudentsWithParentsAndEmergency()
* Picks one emergency contact via MIN(...) with groupBy student.
*/
public function getNewStudentsWithParentsAndEmergency(): array
public static function getNewStudentsWithParentsAndEmergency(): array
{
return $this->select([
'students.*',
'u.firstname AS parent_firstname',
'u.lastname AS parent_lastname',
'u.email AS parent_email',
'u.cellphone AS parent_phone',
// Pick one emergency contact (MIN() works fine if you group by student)
'MIN(ec.emergency_contact_name) AS emergency_name',
'MIN(ec.relation) AS emergency_relationship',
'MIN(ec.cellphone) AS emergency_phone',
])
->join('users u', 'u.id = students.parent_id', 'left')
->join('emergency_contacts ec', 'ec.parent_id = students.parent_id', 'left')
->where('students.is_new', 1) // strictly 1; change to 'Yes' if your data uses Yes/No
return DB::table('students')
->selectRaw('
students.*,
u.firstname AS parent_firstname,
u.lastname AS parent_lastname,
u.email AS parent_email,
u.cellphone AS parent_phone,
MIN(ec.emergency_contact_name) AS emergency_name,
MIN(ec.relation) AS emergency_relationship,
MIN(ec.cellphone) AS emergency_phone
')
->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
->leftJoin('emergency_contacts as ec', 'ec.parent_id', '=', 'students.parent_id')
->where('students.is_new', 1)
->groupBy('students.id')
->orderBy('students.lastname', 'ASC')
->orderBy('students.firstname', 'ASC')
->findAll();
->orderBy('students.lastname', 'asc')
->orderBy('students.firstname', 'asc')
->get()
->map(fn ($r) => (array) $r)
->all();
}
/**
* Fetch students with parent + one emergency contact.
* @param null|int $isNew Pass 1 for only new, 0 for only not-new, null for both.
* CI: getStudentsWithParentsAndEmergency(?schoolYear, ?isNew)
*/
/**
* Fetch students with parent + one emergency contact.
*
* @param null|int $isNew 1 = only new, 0 = only not-new, null = both
* @param null|string $schoolYear e.g. "2025-2026"; pass null or "all" for no filter
* @return array
*/
public function getStudentsWithParentsAndEmergency(?string $schoolYear = null, ?int $isNew = null): array
public static function getStudentsWithParentsAndEmergency(?string $schoolYear = null, ?int $isNew = null): array
{
$builder = $this->select([
'students.*',
'u.firstname AS parent_firstname',
'u.lastname AS parent_lastname',
'u.email AS parent_email',
'u.cellphone AS parent_phone',
'MIN(ec.emergency_contact_name) AS emergency_name',
'MIN(ec.relation) AS emergency_relationship',
'MIN(ec.cellphone) AS emergency_phone',
])
->join('users u', 'u.id = students.parent_id', 'left')
->join('emergency_contacts ec', 'ec.parent_id = students.parent_id', 'left');
$q = DB::table('students')
->selectRaw('
students.*,
u.firstname AS parent_firstname,
u.lastname AS parent_lastname,
u.email AS parent_email,
u.cellphone AS parent_phone,
MIN(ec.emergency_contact_name) AS emergency_name,
MIN(ec.relation) AS emergency_relationship,
MIN(ec.cellphone) AS emergency_phone
')
->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
->leftJoin('emergency_contacts as ec', 'ec.parent_id', '=', 'students.parent_id');
// is_new filter
if ($isNew === 0 || $isNew === 1) {
$builder->where('students.is_new', $isNew);
$q->where('students.is_new', $isNew);
} else {
$builder->whereIn('students.is_new', [0, 1]); // both
$q->whereIn('students.is_new', [0, 1]);
}
// school_year filter (skip if null or "all")
if ($schoolYear !== null && strtolower($schoolYear) !== 'all') {
$builder->where('students.school_year', $schoolYear);
$q->where('students.school_year', $schoolYear);
}
return $builder
->groupBy('students.id')
->orderBy('students.is_new', 'DESC') // new first
->orderBy('students.lastname', 'ASC')
->orderBy('students.firstname', 'ASC')
->findAll();
return $q->groupBy('students.id')
->orderByDesc('students.is_new') // new first
->orderBy('students.lastname', 'asc')
->orderBy('students.firstname', 'asc')
->get()
->map(fn ($r) => (array) $r)
->all();
}
public function getStudentSchoolIdByStudentId(int $studentId): ?string
/** CI: getStudentSchoolIdByStudentId() */
public static function getStudentSchoolIdByStudentId(int $studentId): ?string
{
$result = $this->select('school_id')
->where('id', $studentId)
$v = static::query()->where('id', $studentId)->value('school_id');
return $v !== null ? (string) $v : null;
}
/** CI: getAllStudents() */
public static function getAllStudents()
{
return static::query()->get();
}
/** CI: getTotalStudents() */
public static function getTotalStudents(): int
{
return (int) static::query()->count();
}
/** CI: getFullNameById() => returns [firstname, lastname] or null */
public static function getFullNameById($studentId): ?array
{
$student = static::query()->find($studentId);
if (!$student) return null;
$first = (string) ($student->firstname ?? '');
$last = (string) ($student->lastname ?? '');
if ($first === '' && $last === '') return null;
return ['firstname' => $first, 'lastname' => $last];
}
/** CI: getFullName($student array) */
public static function getFullName(array $student): string
{
return trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? ''));
}
/** CI: getLastRegistrationDate($ParentId) */
public static function getLastRegistrationDate(int $parentId): ?array
{
$row = static::query()
->select('registration_date')
->where('parent_id', $parentId)
->orderByDesc('registration_date')
->first();
return $result['school_id'] ?? null;
return $row ? ['registration_date' => $row->registration_date] : null;
}
/**
* Get all students with assignments, current semester, and school year.
* CI: getByClassAndYear($class_section_id, $school_year, ?semester ignored)
*/
public function getStudentsWithAssignments()
public static function getByClassAndYear(int $classSectionId, string $schoolYear, ?string $semester = null): array
{
// Retrieve school year and semester from the configuration table
$configTable = $this->db->table('configuration');
$schoolYear = $configTable->select('config_value')
->where('config_key', 'school_year')
->get()
->getRowArray()['config_value'];
$studentIds = StudentClass::query()
->select('student_id')
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->pluck('student_id')
->all();
$semester = $configTable->select('config_value')
->where('config_key', 'semester')
->get()
->getRowArray()['config_value'];
if (empty($studentIds)) return [];
return $this->db->table('students')
->select('
students.id,
students.firstname,
students.lastname,
students.age,
users.email,
users.cellphone as phone,
students.registration_grade,
classSection.class_section_name as current_class,
"' . $schoolYear . '" as school_year,
"' . $semester . '" as semester
return static::query()
->whereIn('id', $studentIds)
->where('is_active', 1)
->orderBy('firstname', 'asc')
->orderBy('lastname', 'asc')
->get()
->all();
}
public static function getSchoolIdByStudentId($studentId)
{
return static::query()->where('id', $studentId)->value('school_id');
}
public static function getSchoolIdsByParentId(int $parentId): array
{
return static::query()
->where('parent_id', $parentId)
->distinct()
->pluck('school_id')
->filter(fn ($v) => $v !== null && $v !== '')
->values()
->all();
}
public static function getStudentIdsByParentId(int $parentId): array
{
return static::query()
->where('parent_id', $parentId)
->distinct()
->pluck('id')
->map(fn ($v) => (int) $v)
->values()
->all();
}
/**
* CI: getStudentsWithClassAndEnrollment()
* Keeps your original grouping so behavior doesnt change.
*/
public static function getStudentsWithClassAndEnrollment(): array
{
return DB::table('students')
->selectRaw('
students.*,
student_class.class_section_id,
enrollments.enrollment_status,
enrollments.admission_status,
u.firstname AS parent_firstname,
u.lastname AS parent_lastname
')
->join('users', 'users.id = students.parent_id', 'left')
->join('student_class', 'student_class.student_id = students.id', 'left')
->join('class_section', 'student_class.class_section_id = classSection.id', 'left')
->leftJoin('student_class', 'student_class.student_id', '=', 'students.id')
->leftJoin('enrollments', 'enrollments.student_id', '=', 'students.id')
->leftJoin('users as u', 'u.id', '=', 'students.parent_id')
->groupByRaw('students.id, student_class.class_section_id, enrollments.enrollment_status, enrollments.admission_status, u.firstname, u.lastname')
->get()
->getResultArray();
->map(fn ($r) => (array) $r)
->all();
}
/**
* Get students assigned to a specific teacher.
*
* @param int $teacherId
* @return array
* CI: getStudentInfoByClassSectionId($classSectionId, $semester ignored, $schoolYear optional)
* NOTE: In CI you appended updated_by from session; here we accept actor id.
*/
public function getStudentsByTeacher($teacherId)
public static function getStudentInfoByClassSectionId(int $classSectionId, ?string $semester = null, ?string $schoolYear = null, ?int $actorId = null): array
{
return $this->db->table('students')
->select('
students.firstname,
students.lastname,
students.age,
classSection.class_section_name,
student_class.semester,
student_class.school_year
')
->join('student_class', 'students.id = student_class.student_id', 'left')
->join('teacher_class', 'student_class.class_section_id = teacher_class.class_section_id', 'left')
->join('class_section', 'student_class.class_section_id = classSection.id', 'left')
->where('teacher_class.teacher_id', $teacherId)
->get()
->getResultArray();
}
if ($classSectionId <= 0) return [];
/**
* Get all students in a specific class section.
*
* @param int $classSectionId
* @return array
*/
public function getStudentsByClass($classSectionId)
{
return $this->db->table('students')
->join('student_class', 'students.id = student_class.student_id', 'left')
->where('student_class.class_section_id', $classSectionId)
->select('students.*, student_class.class_section_id')
->get()
->getResultArray();
}
$q = DB::table('students')
->selectRaw('students.id AS student_id, students.school_id, students.firstname, students.lastname, sc.class_section_id')
->join('student_class as sc', 'sc.student_id', '=', 'students.id')
->where('sc.class_section_id', $classSectionId)
->where('students.is_active', 1)
->distinct()
->orderBy('students.lastname', 'asc')
->orderBy('students.firstname', 'asc');
/**
* Get all students.
*
* @return array
*/
public function getAllStudents()
{
return $this->findAll();
}
/**
* Get the total number of students.
*
* @return int
*/
public function getTotalStudents()
{
return $this->countAllResults();
}
public function getFullNameById($studentId)
{
// Fetch student record by ID
$student = $this->find($studentId);
// Return null if student not found
if (empty($student)) {
return null;
if ($schoolYear !== null) {
$q->where('sc.school_year', $schoolYear);
}
// Extract first and last name (support common variations)
$firstName = $student['firstname'] ?? $student['first_name'] ?? $student['first'] ?? '';
$lastName = $student['lastname'] ?? $student['last_name'] ?? $student['last'] ?? '';
$rows = $q->get()->map(fn ($r) => (array) $r)->all();
// Return null if both names are empty
if ($firstName === '' && $lastName === '') {
return null;
$actorId = $actorId ?? (auth()->id() ?? 0);
foreach ($rows as &$row) {
$row['updated_by'] = (int) $actorId;
}
unset($row);
return $rows;
}
/* ============================================================
* Optional validation helper
* ============================================================
*/
public static function rules(bool $updating = false): array
{
$req = $updating ? 'sometimes' : 'required';
// Return names separately
return [
'firstname' => $firstName,
'lastname' => $lastName,
'school_id' => ['nullable', 'integer', 'min:1'],
'firstname' => [$req, 'string', 'max:120'],
'lastname' => [$req, 'string', 'max:120'],
'dob' => ['nullable', 'date'],
'age' => ['nullable', 'integer', 'min:0'],
'gender' => ['nullable', 'string', 'max:20'],
'registration_grade' => ['nullable', 'string', 'max:50'],
'photo_consent' => ['nullable', 'boolean'],
'is_new' => ['nullable', 'boolean'],
'parent_id' => ['nullable', 'integer'],
'school_year' => ['nullable', 'string', 'max:20'],
'registration_date' => ['nullable', 'date'],
'tuition_paid' => ['nullable', 'boolean'],
'year_of_registration'=> ['nullable', 'integer', 'min:0'],
'rfid_tag' => ['nullable', 'string', 'max:120'],
'is_active' => ['nullable', 'boolean'],
];
}
/**
* Get the full name of a student.
*
* @param array $student
* @return string
*/
public function getFullName($student)
{
return ($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '');
}
// In the Student
public function getLastRegistrationDate($ParentId)
{
return $this->select('registration_date')
->where('parent_id', $ParentId)
->orderBy('registration_date', 'DESC') // Order by most recent registration date
->limit(1) // Get only the most recent registration
->first(); // Return the first record
}
public function getByClassAndYear($class_section_id, $school_year): array
{
$studentClass = new \App\Models\StudentClass();
$studentIds = $studentClass
->select('student_id')
->where('class_section_id', $class_section_id)
->where('school_year', $school_year)
->findAll();
$studentIds = array_column($studentIds, 'student_id');
if (empty($studentIds)) {
return [];
}
return $this->whereIn('id', $studentIds)
->orderBy('firstname', 'ASC')
->orderBy('lastname', 'ASC')
->findAll();
}
public function getSchoolIdByStudentId($studentId)
{
return $this->select('school_id')
->where('id', $studentId)
->first()['school_id'] ?? null;
}
public function getSchoolIdsByParentId($parentId)
{
$results = $this->select('school_id')
->where('parent_id', $parentId)
->distinct()
->findAll();
return array_column($results, 'school_id');
}
public function getStudentIdsByParentId($parentId)
{
$results = $this->select('id')
->where('parent_id', $parentId)
->distinct()
->findAll();
return array_column($results, 'id');
}
public function getStudentsWithClassAndEnrollment()
{
return $this->select('
students.*,
student_class.class_section_id,
enrollments.enrollment_status,
enrollments.admission_status,
u.firstname AS parent_firstname,
u.lastname AS parent_lastname
')
->join('student_class', 'student_class.student_id = students.id', 'left')
->join('enrollments', 'enrollments.student_id = students.id', 'left')
->join('users u', 'u.id = students.parent_id', 'left')
// keep your original grouping as-is so behavior doesn't change
->groupBy('students.id, student_class.class_section_id, enrollments.enrollment_status, enrollments.admission_status')
->findAll();
}
/*
public function getStudentsWithClassAndEnrollment()
{
return $this->select('students.*, student_class.class_section_id, enrollments.enrollment_status, enrollments.admission_status')
->join('student_class', 'student_class.student_id = students.id', 'left')
->join('enrollments', 'enrollments.student_id = students.id', 'left')
->groupBy('students.id, student_class.class_section_id, enrollments.enrollment_status, enrollments.admission_status')
->findAll();
}
*/
/**
* Get student and teacher information based on class_section_id.
*
* @param int $classSectionId
* @return array|null
*/
public function getStudentInfoByClassSectionId($classSectionId, ?string $semester = null, ?string $schoolYear = null): array
{
$classSectionId = (int) $classSectionId;
if ($classSectionId <= 0) {
return [];
}
// Start from this model's table (likely 'students')
$builder = $this->builder();
$builder
->select('students.id AS student_id, students.school_id, students.firstname, students.lastname, sc.class_section_id')
->join('student_class sc', 'sc.student_id = students.id', 'inner')
->where('sc.class_section_id', $classSectionId);
// Optional term scoping (safe no-ops if null)
if ($semester !== null) {
$builder->where('sc.semester', $semester);
}
if ($schoolYear !== null) {
$builder->where('sc.school_year', $schoolYear);
}
// Prevent accidental dupes if student_class has multiple rows
$builder->distinct();
$builder->orderBy('students.lastname', 'ASC')
->orderBy('students.firstname', 'ASC');
$results = $builder->get()->getResultArray();
// 🔹 Instead of saving, just add `updated_by` to each result row
$userId = (int) (session()->get('user_id') ?? 0);
if (!empty($results)) {
foreach ($results as &$row) {
$row['updated_by'] = $userId; // add actor id for use downstream
}
unset($row); // good practice to break ref
}
return $results ?: [];
}
// In your existing Student
public function getStudentBasic(int $studentId): ?array
{
return $this->select('id, firstname, lastname, grade')->find($studentId);
}
/**
* Returns students with:
* - class/enrollment/admission info
* - primary parent (guardian) info as aliases the view expects:
* parent_firstname, parent_lastname, parent_phone, parent_email
* - emergency contact info as: emergency_name, emergency_relationship, emergency_phone
*
* Adjust table/column names to your schema where noted.
*/
/*
public function getStudentsWithClassAndEnrollment(): array
{
$db = $this->db;
// ---- Notes: adjust these names if your schema differs ----
// users table fields:
// users.first_name, users.last_name, users.email, users.phone
// students table may already have emergency_* columns; if not, enable the alt join below.
// class/enrollment sources: adjust joins to your real tables/fields.
// ----------------------------------------------------------
$builder = $db->table('students s');
// Base student fields used by the view/controller
$builder->select([
's.id',
's.firstname',
's.lastname',
's.is_new',
's.dob',
's.registration_date',
's.photo_consent',
// If your students table stores class_section_id directly; otherwise replace with a subquery/join
's.class_section_id',
// Optional emergency columns ON students table (comment out if you use separate table)
's.emergency_name',
's.emergency_relationship',
's.emergency_phone',
]);
// ---------- Enrollment / Admission status ----------
// Replace these with your real sources:
// Example 1: statuses live on students (already selected) then alias directly:
// $builder->select('s.enrollment_status, s.admission_status');
// Example 2: join enrollment/admission tables:
// $builder->join('enrollments e', 'e.student_id = s.id AND e.is_current = 1', 'left');
// $builder->select('e.status AS enrollment_status');
// $builder->join('admissions a', 'a.student_id = s.id', 'left');
// $builder->select('a.status AS admission_status');
// If you actually store on students, uncomment this line and remove the example joins above:
$builder->select('s.enrollment_status, s.admission_status');
// ---------- Class Section readable name ----------
// If you already have a helper to resolve the name, the controller uses it.
// But its faster to produce a label here:
// Example: class_sections (id, name), classes (name) etc. Adjust join/concat as needed.
$builder->join('class_sections cs', 'cs.id = s.class_section_id', 'left');
$builder->select("COALESCE(cs.name, 'Class not Assigned') AS class_section");
// ---------- Family/Guardian (primary parent) ----------
// Link student -> family via family_students
$builder->join('family_students fs', 'fs.student_id = s.id', 'left');
// Join to primary guardian for that family
$builder->join('family_guardians fg', 'fg.family_id = fs.family_id AND fg.is_primary = 1', 'left');
// Join to users for the guardian details (ADJUST field names!)
$builder->join('users pu', 'pu.id = fg.user_id', 'left');
// Produce the aliases your view expects
$builder->select([
'pu.first_name AS parent_firstname',
'pu.last_name AS parent_lastname',
'pu.email AS parent_email',
'pu.phone AS parent_phone',
]);
// ---------- OPTIONAL: If emergency contact lives in a separate table ----------
// Comment OUT the s.emergency_* selections above and use this instead:
// $builder->join('student_emergency_contacts sec', 'sec.student_id = s.id AND sec.is_primary = 1', 'left');
// $builder->select([
// 'sec.name AS emergency_name',
// 'sec.relationship AS emergency_relationship',
// 'sec.phone AS emergency_phone',
// ]);
// If you need only currently active / current semester, apply where here:
// $builder->where('s.is_active', 1);
// Avoid duplicate rows (e.g., if multiple guardians slipped in)
$builder->groupBy('s.id');
// Optional: order
$builder->orderBy('s.lastname', 'ASC')->orderBy('s.firstname', 'ASC');
return $builder->get()->getResultArray();
}
*/
}
}