254 lines
7.2 KiB
PHP
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;
|
|
}
|
|
}
|