Files
alrahma_sunday_school/app/Services/StudentYearStatusService.php
T
root 9676261d65
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 48s
Tests / PHPUnit (push) Successful in 1m19s
fix registration enrollment of students
2026-08-26 21:36:03 -04:00

254 lines
7.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\StudentYearStatusModel;
use CodeIgniter\Database\BaseConnection;
class StudentYearStatusService
{
public function __construct(
private BaseConnection $db,
private StudentYearStatusModel $yearStatusModel,
) {
}
public function isNew(int $studentId, string $schoolYear): bool
{
if ($studentId <= 0) {
return true;
}
$schoolYear = trim($schoolYear);
if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear)) {
return true;
}
if (! $this->db->tableExists('student_year_status')) {
return true;
}
$row = $this->yearStatusModel
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->first();
if (! is_array($row)) {
return true;
}
return (int) ($row['is_new'] ?? 1) === 1;
}
public function upsert(int $studentId, string $schoolYear, bool $isNew): bool
{
if ($studentId <= 0) {
return false;
}
$schoolYear = trim($schoolYear);
if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear) || ! $this->db->tableExists('student_year_status')) {
return false;
}
$flag = $isNew ? 1 : 0;
$existing = $this->yearStatusModel
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->first();
if (is_array($existing) && isset($existing['id'])) {
return (bool) $this->yearStatusModel->update((int) $existing['id'], [
'is_new' => $flag,
]);
}
return (bool) $this->yearStatusModel->insert([
'student_id' => $studentId,
'school_year' => $schoolYear,
'is_new' => $flag,
]);
}
public function hasStatus(int $studentId, string $schoolYear): bool
{
if ($studentId <= 0) {
return false;
}
$schoolYear = trim($schoolYear);
if (! preg_match('/^\d{4}-\d{4}$/', $schoolYear) || ! $this->db->tableExists('student_year_status')) {
return false;
}
return $this->yearStatusModel
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->countAllResults() > 0;
}
/**
* Mark students who existed in a source school year as returning in the target year.
*
* This is intentionally idempotent so school-year activation/closing and repair
* migrations can call it safely.
*/
public function markReturningStudentsForTargetYear(string $sourceSchoolYear, string $targetSchoolYear): int
{
$sourceSchoolYear = trim($sourceSchoolYear);
$targetSchoolYear = trim($targetSchoolYear);
if (
! preg_match('/^\d{4}-\d{4}$/', $sourceSchoolYear)
|| ! preg_match('/^\d{4}-\d{4}$/', $targetSchoolYear)
|| $sourceSchoolYear === $targetSchoolYear
|| ! $this->db->tableExists('student_year_status')
) {
return 0;
}
$studentIds = $this->returningStudentIds($sourceSchoolYear);
foreach ($studentIds as $studentId) {
$this->upsert($studentId, $targetSchoolYear, false);
}
return count($studentIds);
}
/**
* @param list<array<string, mixed>> $students
*/
public function attachToStudents(array &$students, string $schoolYear): void
{
$schoolYear = trim($schoolYear);
if ($students === [] || ! preg_match('/^\d{4}-\d{4}$/', $schoolYear)) {
return;
}
$ids = [];
foreach ($students as $student) {
$id = (int) ($student['id'] ?? $student['student_id'] ?? 0);
if ($id > 0) {
$ids[$id] = true;
}
}
$flags = $this->flagsForStudents(array_keys($ids), $schoolYear);
foreach ($students as &$student) {
$id = (int) ($student['id'] ?? $student['student_id'] ?? 0);
if ($id <= 0) {
continue;
}
$student['is_new'] = $flags[$id] ?? 1;
}
unset($student);
}
/**
* @param list<int> $studentIds
* @return array<int, int>
*/
public function flagsForStudents(array $studentIds, string $schoolYear): array
{
$studentIds = array_values(array_unique(array_filter(
array_map('intval', $studentIds),
static fn(int $id): bool => $id > 0
)));
$schoolYear = trim($schoolYear);
if ($studentIds === [] || ! preg_match('/^\d{4}-\d{4}$/', $schoolYear)) {
return [];
}
$flags = [];
foreach ($studentIds as $studentId) {
$flags[$studentId] = 1;
}
if (! $this->db->tableExists('student_year_status')) {
return $flags;
}
$rows = $this->yearStatusModel
->select('student_id, is_new')
->whereIn('student_id', $studentIds)
->where('school_year', $schoolYear)
->findAll();
foreach ($rows as $row) {
$id = (int) ($row['student_id'] ?? 0);
if ($id > 0) {
$flags[$id] = (int) ($row['is_new'] ?? 1) === 1 ? 1 : 0;
}
}
return $flags;
}
/**
* @return list<int>
*/
private function returningStudentIds(string $sourceSchoolYear): array
{
$ids = [];
foreach (['student_year_status', 'student_class', 'enrollments'] as $table) {
if (! $this->db->tableExists($table) || ! $this->db->fieldExists('school_year', $table)) {
continue;
}
$rows = $this->db->table($table)
->select('student_id')
->where('school_year', $sourceSchoolYear)
->where('student_id IS NOT NULL', null, false)
->groupBy('student_id')
->get()
->getResultArray();
foreach ($rows as $row) {
$studentId = (int) ($row['student_id'] ?? 0);
if ($studentId > 0) {
$ids[$studentId] = true;
}
}
}
return array_keys($ids);
}
public function activeSchoolYear(): ?string
{
if ($this->db->tableExists('school_years')) {
$row = $this->db->table('school_years')
->select('name')
->where('status', 'active')
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
$name = trim((string) ($row['name'] ?? ''));
if (preg_match('/^\d{4}-\d{4}$/', $name)) {
return $name;
}
}
if (! $this->db->tableExists('configuration')) {
return null;
}
$row = $this->db->table('configuration')
->select('config_value')
->where('config_key', 'school_year')
->orderBy('id', 'DESC')
->get(1)
->getRowArray();
$name = trim((string) ($row['config_value'] ?? ''));
return preg_match('/^\d{4}-\d{4}$/', $name) ? $name : null;
}
}