Files
alrahma_sunday_school_api/app/Services/Students/StudentAssignmentService.php
T
2026-06-09 01:25:14 -04:00

317 lines
10 KiB
PHP

<?php
namespace App\Services\Students;
use App\Models\ClassSection;
use App\Models\Enrollment;
use App\Models\Student;
use App\Models\StudentClass;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class StudentAssignmentService
{
public function __construct(private StudentConfigService $configService) {}
public function assignClasses(int $studentId, array $classSectionIds, bool $isEventOnly, ?int $userId = null): array
{
$context = $this->configService->context();
$semester = (string) ($context['semester'] ?? '');
$schoolYear = (string) ($context['school_year'] ?? '');
$classSectionIds = $this->normalizeIds($classSectionIds);
if ($studentId <= 0 || empty($classSectionIds) || $schoolYear === '') {
return ['ok' => false, 'message' => 'Missing required data (student/class section).'];
}
$student = Student::query()->find($studentId);
if (! $student) {
return ['ok' => false, 'message' => 'Student not found.'];
}
$sections = ClassSection::query()
->whereIn('class_section_id', $classSectionIds)
->orWhereIn('id', $classSectionIds)
->get()
->toArray();
if (empty($sections)) {
return ['ok' => false, 'message' => 'Class/Section not found.'];
}
$sectionMap = [];
foreach ($sections as $section) {
$sectionMap[(int) ($section['class_section_id'] ?? 0)] = $section;
}
$missing = array_diff($classSectionIds, array_keys($sectionMap));
if (! empty($missing)) {
return ['ok' => false, 'message' => 'One or more selected classes do not exist.'];
}
$existing = StudentClass::query()
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->get()
->keyBy(fn ($row) => (int) ($row->class_section_id ?? 0));
$primarySectionId = $classSectionIds[0];
$primarySection = $sectionMap[$primarySectionId] ?? reset($sectionMap);
$parentClassId = (int) ($primarySection['class_id'] ?? 0);
if ($parentClassId <= 0) {
$parentClassId = (int) (ClassSection::getClassId($primarySectionId) ?? 0);
}
$attendanceStats = [];
$scoreStats = [];
DB::transaction(function () use (
$studentId,
$schoolYear,
$classSectionIds,
$isEventOnly,
$userId,
$existing,
$semester,
$primarySectionId,
$parentClassId,
&$attendanceStats,
&$scoreStats
): void {
foreach ($classSectionIds as $classSectionId) {
$eventFlag = $isEventOnly ? 1 : 0;
if ($existing->has($classSectionId)) {
$eventFlag = (int) ($existing[$classSectionId]->is_event_only ?? 0);
}
$payload = [
'student_id' => $studentId,
'class_section_id' => $classSectionId,
'school_year' => $schoolYear,
'semester' => $semester,
'is_event_only' => $eventFlag,
'updated_by' => $userId,
'updated_at' => now(),
];
if ($existing->has($classSectionId)) {
$existing[$classSectionId]->fill($payload)->save();
} else {
StudentClass::query()->create($payload + [
'created_at' => now(),
]);
}
}
if (! $isEventOnly) {
$enrollment = Enrollment::query()
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->when($semester !== '', fn ($q) => $q->where('semester', $semester))
->first();
if ($enrollment) {
$enrollment->update([
'class_section_id' => $primarySectionId,
'enrollment_status' => 'payment pending',
'admission_status' => 'accepted',
'updated_at' => now(),
]);
}
$attendanceStats = $this->updateAttendance(
$studentId,
$primarySectionId,
$parentClassId,
$semester,
$schoolYear,
$userId
);
$scoreStats = $this->updateScores(
$studentId,
$primarySectionId,
$semester,
$schoolYear,
$userId
);
}
});
$displayNames = [];
foreach ($classSectionIds as $classSectionId) {
$section = $sectionMap[$classSectionId] ?? null;
if (! $section) {
continue;
}
$name = (string) ($section['class_section_name'] ?? '');
if ($name === '') {
$name = 'Section #'.$classSectionId;
}
$displayNames[] = $name;
}
return [
'ok' => true,
'student_id' => $studentId,
'class_section_id' => $primarySectionId,
'class_section_ids' => $classSectionIds,
'class_section_names' => $displayNames,
'attendance_updates' => $attendanceStats,
'score_updates' => $scoreStats,
];
}
public function removeClass(int $studentId, int $classSectionId, ?int $userId = null): array
{
$context = $this->configService->context();
$semester = (string) ($context['semester'] ?? '');
$schoolYear = (string) ($context['school_year'] ?? '');
if ($studentId <= 0 || $classSectionId <= 0 || $schoolYear === '') {
return ['ok' => false, 'message' => 'Missing required data (student/class section).'];
}
$row = StudentClass::query()
->where('student_id', $studentId)
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->first();
if (! $row) {
return ['ok' => false, 'message' => 'Assignment not found for this student/class.'];
}
$remainingIds = [];
$remainingNames = [];
DB::transaction(function () use (
$studentId,
$classSectionId,
$schoolYear,
$semester,
$userId,
&$remainingIds,
&$remainingNames
): void {
StudentClass::query()
->where('student_id', $studentId)
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->delete();
$remainingIds = StudentClass::getClassSectionIdsByStudentId($studentId, $schoolYear);
$remainingNames = StudentClass::getClassSectionsByStudentId($studentId, $schoolYear, true);
$enrollment = Enrollment::query()
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->when($semester !== '', fn ($q) => $q->where('semester', $semester))
->first();
if ($enrollment) {
$newClassId = ! empty($remainingIds) ? $remainingIds[0] : null;
if ((int) $enrollment->class_section_id === $classSectionId || $newClassId !== null) {
$enrollment->update([
'class_section_id' => $newClassId,
'updated_at' => now(),
'updated_by' => $userId,
]);
}
}
});
return [
'ok' => true,
'student_id' => $studentId,
'removed_class_id' => $classSectionId,
'remaining_ids' => $remainingIds,
'remaining_names' => $remainingNames,
];
}
private function updateAttendance(
int $studentId,
int $classSectionId,
int $classId,
string $semester,
string $schoolYear,
?int $modifiedBy
): array {
$now = now()->toDateTimeString();
$attendanceUpdated = DB::table('attendance_data')
->where('student_id', $studentId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->update([
'class_section_id' => $classSectionId,
'class_id' => $classId,
'updated_at' => $now,
'modified_by' => $modifiedBy,
]);
$recordUpdated = DB::table('attendance_record')
->where('student_id', $studentId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->update([
'class_section_id' => $classSectionId,
'updated_at' => $now,
'modified_by' => $modifiedBy,
]);
return [
'attendance_data_updated' => $attendanceUpdated,
'attendance_record_updated' => $recordUpdated,
];
}
private function updateScores(
int $studentId,
int $classSectionId,
string $semester,
string $schoolYear,
?int $modifiedBy
): array {
$now = now()->toDateTimeString();
$tables = [
'homework',
'quiz',
'project',
'participation',
'midterm_exam',
'final_exam',
'final_score',
'semester_scores',
];
$results = [];
foreach ($tables as $table) {
$payload = [
'class_section_id' => $classSectionId,
'updated_at' => $now,
];
if ($modifiedBy !== null && Schema::hasColumn($table, 'updated_by')) {
$payload['updated_by'] = $modifiedBy;
}
$results[$table.'_updated'] = DB::table($table)
->where('student_id', $studentId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->update($payload);
}
return $results;
}
private function normalizeIds(array $ids): array
{
$values = array_map('intval', $ids);
$values = array_values(array_unique(array_filter($values, static fn ($v) => $v > 0)));
return $values;
}
}