Files
root 58726ee0e9
API CI/CD / Validate (composer + pint) (push) Successful in 3m6s
API CI/CD / Test (PHPUnit) (push) Failing after 5m48s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 1m33s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
fix tables to have school year, and remove school year from other tables.
2026-07-06 22:27:21 -04:00

158 lines
5.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Badges;
use App\Models\Student;
use App\Models\StudentClass;
use Illuminate\Support\Facades\DB;
class BadgeStudentLookupService
{
public function __construct(
protected BadgeTextFormatter $formatter
) {}
/**
* Active student roster for the badge picker.
*
* @param int[] $selectedStudentIds
* @return array<int, array<string, mixed>>
*/
public function fetchStudentList(?string $schoolYear, array $selectedStudentIds = []): array
{
$selectedStudentIds = $this->formatter->normalizeIds($selectedStudentIds);
$classSectionAggregate = DB::connection()->getDriverName() === 'sqlite'
? 'GROUP_CONCAT(DISTINCT cs.class_section_name) as class_section_name'
: "GROUP_CONCAT(DISTINCT cs.class_section_name ORDER BY cs.class_section_name SEPARATOR ', ') as class_section_name";
$query = DB::table('students')
->leftJoin('student_class as sc', function ($join) use ($schoolYear) {
$join->on('sc.student_id', '=', 'students.id');
if (! empty($schoolYear)) {
$join->where('sc.school_year', '=', $schoolYear);
}
})
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id')
->select([
'students.id as student_id',
'students.firstname',
'students.lastname',
'students.registration_grade',
'students.school_id',
DB::raw('COALESCE(MAX(sc.school_year), ?) as school_year'),
DB::raw($classSectionAggregate),
])
->addBinding((string) ($schoolYear ?? ''), 'select')
->where('students.is_active', 1)
->groupBy(
'students.id',
'students.firstname',
'students.lastname',
'students.registration_grade',
'students.school_id'
)
->orderBy('students.lastname')
->orderBy('students.firstname');
if (! empty($selectedStudentIds)) {
$query->whereIn('students.id', $selectedStudentIds);
} elseif (! empty($schoolYear)) {
$query->whereNotNull('sc.student_id');
}
$rows = array_map(static fn ($row) => (array) $row, $query->get()->all());
$out = [];
foreach ($rows as $row) {
$studentId = (int) ($row['student_id'] ?? 0);
if ($studentId <= 0) {
continue;
}
$schoolId = trim((string) ($row['school_id'] ?? ''));
if ($schoolId === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) {
continue;
}
$out[] = [
'student_id' => $studentId,
'firstname' => (string) ($row['firstname'] ?? ''),
'lastname' => (string) ($row['lastname'] ?? ''),
'role_name' => 'Student',
'roles' => 'Student',
'registration_grade' => (string) ($row['registration_grade'] ?? ''),
'class_section_name' => $this->formatter->norm((string) ($row['class_section_name'] ?? '')),
'school_id' => $schoolId,
'school_year' => (string) ($row['school_year'] ?? $schoolYear ?? ''),
];
}
return $out;
}
/**
* Load students for badge printing: valid school_id (QR payload), ordered by name.
*
* @param int[] $studentIds
* @return array<int, array{student_id:int, fullname:string, grade:string, academic_year:string, school_id:string}>
*/
public function fetchForBadges(array $studentIds, ?string $schoolYear): array
{
$studentIds = $this->formatter->normalizeIds($studentIds);
if ($studentIds === []) {
return [];
}
$rows = Student::query()
->whereIn('id', $studentIds)
->where('is_active', 1)
->orderBy('lastname')
->orderBy('firstname')
->get();
$out = [];
foreach ($rows as $student) {
$schoolId = trim((string) ($student->school_id ?? ''));
if ($schoolId === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) {
continue;
}
$fullname = Student::getFullName($student->toArray());
$grade = $this->resolveGrade((int) $student->id, (string) ($student->registration_grade ?? ''));
$academicYear = $schoolYear ?? (string) ($student->school_year ?? '');
$academicYear = $this->formatter->norm($academicYear);
$out[] = [
'student_id' => (int) $student->id,
'fullname' => $fullname !== '' ? $fullname : 'Student',
'grade' => $grade,
'academic_year' => $academicYear,
'school_id' => $schoolId,
];
}
return $out;
}
protected function resolveGrade(int $studentId, string $registrationGrade): string
{
$registrationGrade = $this->formatter->norm($registrationGrade);
if ($registrationGrade !== '') {
return $registrationGrade;
}
try {
$g = StudentClass::getStudentGrade($studentId);
$g = $this->formatter->norm((string) $g);
return $g !== '' ? $g : '—';
} catch (\Throwable) {
return '—';
}
}
}