Files
alrahma_sunday_school_api/app/Services/Administrator/TeacherSubmissionNotificationService.php
T
2026-06-09 00:03:03 -04:00

221 lines
8.3 KiB
PHP

<?php
namespace App\Services\Administrator;
use App\Mail\TeacherSubmissionReminderMail;
use App\Models\ClassSection;
use App\Models\TeacherSubmissionNotificationHistory;
use App\Models\User;
use App\Services\ApplicationUrlService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class TeacherSubmissionNotificationService
{
public function __construct(
protected AdministratorSharedService $shared,
protected TeacherSubmissionSupportService $support,
protected ApplicationUrlService $urls,
) {}
public function send(Request $request, int $adminId): array
{
$notify = $request->input('notify');
if (! is_array($notify)) {
return [
'success' => false,
'message' => 'Select at least one teacher to notify.',
'status' => 422,
];
}
$missingItemsPayload = $request->input('missing_items', []);
$schoolYear = $this->shared->getSchoolYear($request->input('school_year'));
$semester = $this->shared->getSemester($request->input('semester'));
$targets = $this->extractTargets($notify, $schoolYear, $semester);
if (empty($targets)) {
return [
'success' => false,
'message' => 'Select at least one teacher to notify.',
'status' => 422,
];
}
$teacherIds = array_values(array_unique(array_column($targets, 'teacher_id')));
$classSectionIds = array_values(array_unique(array_column($targets, 'class_section_id')));
$classSections = ClassSection::query()
->select('class_section_id', 'class_section_name')
->whereIn('class_section_id', $classSectionIds)
->get()
->keyBy('class_section_id');
$teachers = User::query()
->select('id', 'firstname', 'lastname', 'email')
->whereIn('id', $teacherIds)
->get()
->keyBy('id');
$adminUser = User::find($adminId);
$adminName = trim(($adminUser->firstname ?? '').' '.($adminUser->lastname ?? '')) ?: 'Administrator';
$scoreUrl = $this->urls->docsHomeUrl();
$sentCount = 0;
$failCount = 0;
foreach ($targets as $target) {
$classSectionId = (int) $target['class_section_id'];
$teacherId = (int) $target['teacher_id'];
$teacher = $teachers->get($teacherId);
$sectionName = $classSections->get($classSectionId)?->class_section_name ?? "Section {$classSectionId}";
$teacherName = trim(($teacher->firstname ?? '').' '.($teacher->lastname ?? '')) ?: 'Teacher';
$missingItems = $this->resolveMissingItems($missingItemsPayload, $classSectionId, $teacherId);
$missingNote = ! empty($missingItems)
? '<p>Outstanding items: '.e($this->support->formatMissingItemsText($missingItems)).'.</p>'
: '<p>Our records show no outstanding submissions for this section, but please verify if anything still needs attention.</p>';
$subject = "Reminder: Complete submissions for {$sectionName}";
$body = "<p>Dear {$teacherName},</p>"
."<p>Administration is gently reminding you to wrap up any remaining score submissions and/or comments for {$sectionName}.</p>"
.$missingNote
."<p>Visit <a href=\"{$scoreUrl}\">Teacher Score Submission</a> to address any remaining items.</p>"
."<p>Thank you,<br>{$adminName}</p>";
$email = $teacher->email ?? '';
$status = 'failed';
if (! empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
try {
Mail::to($email)->send(new TeacherSubmissionReminderMail($subject, $body));
$status = 'sent';
} catch (\Throwable $e) {
Log::error('Teacher submission notification failed: '.$e->getMessage());
}
}
if ($status === 'sent') {
$sentCount++;
} else {
$failCount++;
}
TeacherSubmissionNotificationHistory::create([
'teacher_id' => $teacherId,
'class_section_id' => $classSectionId,
'admin_id' => $adminId,
'notification_category' => 'teacher_submissions',
'message' => $this->support->truncateNotificationMessage($body),
'status' => $status,
'school_year' => $schoolYear,
'semester' => $semester,
'sent_at' => now(),
]);
}
$parts = [];
if ($sentCount > 0) {
$parts[] = $sentCount.' reminder'.($sentCount === 1 ? '' : 's').' sent';
}
if ($failCount > 0) {
$parts[] = $failCount.' reminder'.($failCount === 1 ? '' : 's').' failed';
}
return [
'success' => $failCount === 0,
'message' => ! empty($parts) ? implode(' and ', $parts) : 'No notifications were sent.',
'sent' => $sentCount,
'failed' => $failCount,
'status' => $failCount === 0 ? 200 : 207,
];
}
protected function extractTargets(array $notify, string $schoolYear, string $semester): array
{
if ($this->isFlatTeacherIdList($notify)) {
$teacherIds = array_values(array_unique(array_map('intval', array_filter($notify, static fn ($value) => (int) $value > 0))));
if (empty($teacherIds)) {
return [];
}
return ClassSection::query()
->select('teacher_class.class_section_id', 'teacher_class.teacher_id')
->join('teacher_class', 'teacher_class.class_section_id', '=', 'classSection.class_section_id')
->whereIn('teacher_class.teacher_id', $teacherIds)
->where('teacher_class.school_year', $schoolYear)
->where('teacher_class.semester', $semester)
->orderBy('teacher_class.class_section_id')
->get()
->map(static fn ($row): array => [
'class_section_id' => (int) $row->class_section_id,
'teacher_id' => (int) $row->teacher_id,
])
->filter(static fn (array $row): bool => $row['class_section_id'] > 0 && $row['teacher_id'] > 0)
->values()
->all();
}
$targets = [];
foreach ($notify as $sectionIdRaw => $teachers) {
$sectionId = (int) $sectionIdRaw;
if ($sectionId <= 0 || ! is_array($teachers)) {
continue;
}
foreach ($teachers as $teacherIdRaw => $value) {
$teacherId = (int) $teacherIdRaw;
if ($teacherId <= 0 || $value === null || $value === '') {
continue;
}
$targets["{$sectionId}_{$teacherId}"] = [
'class_section_id' => $sectionId,
'teacher_id' => $teacherId,
];
}
}
return array_values($targets);
}
protected function isFlatTeacherIdList(array $notify): bool
{
foreach ($notify as $value) {
if (is_array($value)) {
return false;
}
}
return true;
}
protected function resolveMissingItems($payload, int $classSectionId, int $teacherId): array
{
if (! is_array($payload)) {
return [];
}
if (isset($payload[$classSectionId]) && is_array($payload[$classSectionId])) {
$sectionPayload = $payload[$classSectionId];
if (array_key_exists($teacherId, $sectionPayload) || array_key_exists((string) $teacherId, $sectionPayload)) {
$value = $sectionPayload[$teacherId] ?? $sectionPayload[(string) $teacherId] ?? null;
return $this->support->parseMissingItemsPayload($value);
}
}
if (array_key_exists($teacherId, $payload) || array_key_exists((string) $teacherId, $payload)) {
$value = $payload[$teacherId] ?? $payload[(string) $teacherId] ?? null;
return $this->support->parseMissingItemsPayload($value);
}
return [];
}
}