Files
alrahma_sunday_school/app/Models/TeacherClassModel.php
T
2026-02-10 22:11:06 -05:00

410 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Models;
use CodeIgniter\Model;
class TeacherClassModel extends Model
{
protected $table = 'teacher_class';
protected $primaryKey = 'id';
protected $allowedFields = [
'teacher_id',
'class_section_id',
'school_year',
'position', // NEW
'updated_by',
'updated_at',
'created_at'
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $skipValidation = false;
/** Request-lifetime memo cache */
protected array $assignedCache = [];
protected array $assignedBySectionCache = [];
protected $validationRules = [
'teacher_id' => 'required|integer',
'class_section_id' => 'required|integer',
'school_year' => 'required|string|max_length[9]',
'position' => 'required|in_list[main,ta]',
];
protected $validationMessages = [
'teacher_id' => [
'required' => 'The teacher ID is required.',
'integer' => 'The teacher ID must be an integer.',
],
'class_section_id' => [
'required' => 'The class section ID is required.',
'integer' => 'The class section ID must be an integer.',
],
'school_year' => [
'required' => 'The school year is required.',
'max_length' => 'The school year cannot exceed 9 characters.',
],
'position' => [
'required' => 'The position (main or ta) is required.',
'in_list' => 'The position must be either "main" or "ta".',
],
];
public function getClassSectionIdByUserId($user_id)
{
$result = $this->where('teacher_id', $user_id)->first();
if (!$result) {
log_message('error', "No class section found for user ID: $user_id");
return null;
}
return $result;
}
public function getClassAssignmentsByUserId(int $userId, ?string $schoolYear = null, ?string $semester = null): array
{
$builder = $this->db->table('teacher_class tc')
->select([
'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 cs', 'tc.class_section_id = cs.class_section_id', 'inner')
->join('classes c', 'cs.class_id = c.id', 'left')
->where('tc.teacher_id', $userId)
->where('tc.class_section_id IS NOT NULL', null, false)
->orderBy('cs.class_section_name', 'ASC');
if ($schoolYear) $builder->where('tc.school_year', $schoolYear);
$rows = $builder->get()->getResultArray();
$out = [];
foreach ($rows as $r) {
$out[] = [
'class_section_pk' => (int)$r['class_section_pk'],
'class_section_id' => (int)$r['class_section_id'],
'class_section_name' => $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($r['position']), // "Main" or "Ta"
'school_year' => $r['school_year'],
'semester' => $semester,
];
}
return $out;
}
public function getClassSectionsByTeacherId($teacherId)
{
$builder = $this->db->table('teacher_class')
->select('classSection.class_section_name, teacher_class.teacher_id, teacher_class.position')
->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id')
->where('teacher_class.teacher_id', $teacherId)
->where('teacher_class.class_section_id IS NOT NULL', null, false);
$result = $builder->get()->getResultArray();
if ($result) {
$sectionNames = array_map(fn($row) => $row['class_section_name'], $result);
$position = $result[0]['position'] ?? '';
return [
'role' => ucfirst($position),
'class_section_name' => implode(', ', $sectionNames)
];
} else {
return ['role' => '', 'class_section_name' => 'No class assigned'];
}
}
public function getClassByTeacherId($teacherId)
{
$builder = $this->db->table('teacher_class')
->select('classSection.class_section_id, classSection.class_section_name')
->join('classSection', 'teacher_class.class_section_id = classSection.class_section_id')
->where('teacher_class.teacher_id', $teacherId)
->where('teacher_class.class_section_id IS NOT NULL', null, false);
return $builder->get()->getResultArray() ?? [];
}
public function getTeacherIdByClassSection(string $classSectionName, string $semester, string $schoolYear)
{
$db = \Config\Database::connect();
$classSectionName = trim($classSectionName);
if (strpos($classSectionName, ',') !== false) {
$parts = array_filter(array_map('trim', explode(',', $classSectionName)));
if (!empty($parts)) {
$classSectionName = reset($parts); // pick the first section as primary
}
}
if ($classSectionName === '') {
return null;
}
$section = $db->table('classSection')
->select('class_section_id')
->where('class_section_name', $classSectionName)
->get()
->getRowArray();
if (!$section) return null;
$result = $db->table('teacher_class')
->select('teacher_id')
->where([
'class_section_id' => $section['class_section_id'],
'school_year' => $schoolYear,
'position' => 'main'
])
->get()
->getRowArray();
return $result['teacher_id'] ?? null;
}
public function getMainTeacherBySection(string $classSectionName, string $semester, string $schoolYear): ?array
{
$db = \Config\Database::connect();
// Adjust table/column names if yours differ (e.g., cs.id vs cs.class_section_id)
$row = $db->table('classSection cs')
->select('u.id AS teacher_id, u.firstname, u.lastname')
->join('teacher_class tc', 'tc.class_section_id = cs.id', 'inner')
->join('users u', 'u.id = tc.teacher_id', 'inner')
->where('cs.class_section_name', $classSectionName)
->where('tc.school_year', $schoolYear)
->where('tc.position', 'main')
->get()
->getRowArray();
return $row ?: null; // ['teacher_id' => ..., 'firstname' => ..., 'lastname' => ...] or null
}
/**
* Return teacher + TAs for a class section in the current term.
* Output:
* [
* 'teacher_id' => int|null,
* 'teacher_name' => string, // 'N/A' if none found
* 'tas' => [ ['id'=>int,'name'=>string], ... ],
* ]
*/
public function getTeacherTABySection(int|string $classSectionId, string $semester, string $schoolYear): array
{
$db = \Config\Database::connect();
// We can read everything straight from teacher_class; no need to join classSection.
$rows = $db->table('teacher_class tc')
->select('u.id AS user_id, u.firstname, u.lastname, u.gender, tc.position')
->join('users u', 'u.id = tc.teacher_id', 'inner')
->where('tc.class_section_id', $classSectionId)
->where('tc.school_year', $schoolYear)
// Prefer the most recent assignment and keep main/co-teachers ahead of assistants
->orderBy("CASE WHEN LOWER(tc.position) IN ('main','teacher','main teacher') THEN 0 ELSE 1 END", 'ASC', false)
->orderBy('COALESCE(tc.updated_at, tc.id)', 'DESC', false)
->orderBy('tc.id', 'DESC')
->get()
->getResultArray();
$teacherId = null;
$teacherName = 'N/A';
$teacherNames = [];
$tas = [];
$firstSeen = null; // fallback if no explicit "main"/"teacher"
$taSeen = [];
$mainSeen = [];
$withPrefix = static function (array $row): string {
$first = trim((string)($row['firstname'] ?? ''));
$last = trim((string)($row['lastname'] ?? ''));
$gender = strtolower(trim((string)($row['gender'] ?? '')));
$name = trim($first . ' ' . $last);
if ($name === '') return '';
$prefix = ($gender === 'female') ? 'Sr ' : (($gender === 'male') ? 'Br ' : '');
return trim($prefix . $name);
};
foreach ($rows as $r) {
$full = $withPrefix($r);
if ($full === '') continue;
if ($firstSeen === null) {
$firstSeen = ['id' => (int)$r['user_id'], 'name' => $full];
}
$pos = strtolower((string)($r['position'] ?? ''));
// Treat either 'main' or legacy 'teacher' as the main teacher role.
if (in_array($pos, ['main', 'teacher', 'main teacher'], true)) {
$uid = (int)$r['user_id'];
if (!isset($mainSeen[$uid])) {
$teacherNames[] = $full;
$mainSeen[$uid] = true;
}
if ($teacherId === null) {
$teacherId = $uid;
$teacherName = $full;
}
continue;
}
// Treat 'ta', 'assistant', 'teacher_assistant' (and similar) as TA roles.
if (in_array($pos, ['ta', 'assistant', 'teacher_assistant'], true)) {
$tid = (int)$r['user_id'];
if (!isset($taSeen[$tid])) {
$tas[] = ['id' => $tid, 'name' => $full];
$taSeen[$tid] = true;
}
continue;
}
// Unknown/missing position: don't discard; well use firstSeen as a fallback later.
}
// Fallback: if no explicit main teacher found, use the first record seen.
if ($teacherId === null && $firstSeen !== null) {
$teacherId = $firstSeen['id'];
$teacherName = $firstSeen['name'];
$teacherNames[] = $firstSeen['name'];
}
// Ensure uniqueness/cleaning
$teacherNames = array_values(array_unique(array_filter(array_map('trim', $teacherNames))));
return [
'teacher_id' => $teacherId,
'teacher_name' => $teacherName,
'teacher_names' => $teacherNames,
'tas' => $tas,
];
}
public function assignTeacherToClass($teacherId, $classSectionId, $semester, $schoolYear, $position = 'main')
{
if (!in_array($position, ['main', 'ta'])) {
throw new \InvalidArgumentException('Position must be either "main" or "ta"');
}
$existing = $this->where([
'teacher_id' => $teacherId,
'class_section_id' => $classSectionId,
'school_year' => $schoolYear,
'position' => $position
])->first();
if ($existing) {
return $this->update($existing['id'], ['updated_at' => utc_now()]);
}
return $this->insert([
'teacher_id' => $teacherId,
'class_section_id' => $classSectionId,
'school_year' => $schoolYear,
'position' => $position,
'created_at' => utc_now(),
'updated_at' => utc_now()
]);
}
public function findAll($limit = 0, $offset = 0)
{
return parent::findAll($limit, $offset);
}
public function getAssignedClassSections(int $teacherId, string $schoolYear, string $semester)
{
return $this->select('teacher_class.class_section_id, teacher_class.position, classSection.class_section_name')
->join('classSection', 'classSection.id = teacher_class.class_section_id')
->where('teacher_class.teacher_id', $teacherId)
->where('teacher_class.school_year', $schoolYear)
->findAll();
}
/**
* Get assigned teachers (with names) for ONE section code + term.
* Returns rows: class_section_id, teacher_id, position, firstname, lastname
*/
public function assignedForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
{
$key = $sectionCode . '|' . $semester . '|' . $schoolYear;
if (isset($this->assignedCache[$key])) {
return $this->assignedCache[$key];
}
$rows = $this->db->table('teacher_class tc')
->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
->join('users u', 'u.id = tc.teacher_id', 'left')
->where('tc.class_section_id', $sectionCode) // SECTION CODE
->where('tc.school_year', $schoolYear)
->orderBy("FIELD(tc.position, 'main','ta')", '', false) // main → ta
->orderBy('u.firstname', 'ASC')
->get()->getResultArray();
return $this->assignedCache[$key] = $rows;
}
/**
* Convenience: same as above but keyed by teacher_id.
* Returns: [teacher_id] => row
*/
public function assignedMapForSectionTerm(int $sectionCode, string $semester, string $schoolYear): array
{
$out = [];
foreach ($this->assignedForSectionTerm($sectionCode, $semester, $schoolYear) as $r) {
$out[(int)$r['teacher_id']] = $r;
}
return $out;
}
/**
* Batch helper for a whole term (optionally filtered to some sections).
* Returns: [sectionCode] => [rows...]
*/
public function assignedBySectionForTerm(string $semester, string $schoolYear, ?array $onlySectionCodes = null): array
{
$cacheKey = ($onlySectionCodes ? implode(',', array_map('intval', $onlySectionCodes)) : '*') . '|' . $semester . '|' . $schoolYear;
if (isset($this->assignedBySectionCache[$cacheKey])) {
return $this->assignedBySectionCache[$cacheKey];
}
$qb = $this->db->table('teacher_class tc')
->select('tc.class_section_id, tc.teacher_id, tc.position, u.firstname, u.lastname')
->join('users u', 'u.id = tc.teacher_id', 'left')
->where('tc.school_year', $schoolYear)
->orderBy('tc.class_section_id', 'ASC')
->orderBy("FIELD(tc.position, 'main','ta')", '', false)
->orderBy('u.firstname', 'ASC');
if (!empty($onlySectionCodes)) {
$qb->whereIn('tc.class_section_id', array_map('intval', $onlySectionCodes));
}
$rows = $qb->get()->getResultArray();
$out = [];
foreach ($rows as $r) {
$code = (int)$r['class_section_id'];
$out[$code][] = $r;
}
return $this->assignedBySectionCache[$cacheKey] = $out;
}
}