false, 'message' => 'Missing student id.']; } $rows = DB::select( "SELECT u.firstname, u.lastname, u.email, fg.is_primary FROM family_students fs JOIN family_guardians fg ON fg.family_id = fs.family_id JOIN users u ON u.id = fg.user_id WHERE fs.student_id = ? AND fg.receive_emails = 1 AND u.email IS NOT NULL AND u.email != '' ORDER BY fg.is_primary DESC, u.lastname, u.firstname", [$studentId] ); if (empty($rows)) { Log::warning('Below60Email: no guardian emails', ['student_id' => $studentId]); return ['ok' => false, 'message' => 'No guardian email addresses.']; } $emails = []; foreach ($rows as $row) { $email = trim((string) ($row->email ?? '')); if ($email !== '') { $emails[$email] = true; } } $emails = array_keys($emails); $primary = $rows[0] ?? null; $parentName = ''; if ($primary) { $parentName = trim((string) ($primary->firstname ?? '') . ' ' . (string) ($primary->lastname ?? '')); } $subject = (string) ($payload['subject'] ?? ''); if ($subject === '') { $subject = 'Student Performance Alert'; if ($studentName !== '') { $subject .= ' — ' . $studentName; } if ($semester !== '' || $schoolYear !== '') { $subject .= ' (' . trim($semester . ' ' . $schoolYear) . ')'; } } $emailData = [ 'title' => $subject, 'parent_name' => $parentName !== '' ? $parentName : 'Parent/Guardian', 'student_name' => $studentName !== '' ? $studentName : 'your student', 'class_section_name' => $classSection, 'semester' => $semester, 'school_year' => $schoolYear, 'scores' => $scores, 'comment' => $comment, 'sent_at' => utc_now(), ]; $html = trim((string) ($payload['html'] ?? '')); if ($html === '') { $html = MailHtml::document($subject, MailHtml::belowSixtyInnerHtml($emailData)); } $sent = 0; foreach ($emails as $to) { $ok = $this->emailService->send($to, $subject, $html, 'general'); if ($ok) { $sent++; } } Log::info('Below60Email dispatch', [ 'student_id' => $studentId, 'sent' => $sent, 'recipients' => count($emails), ]); return [ 'ok' => $sent > 0, 'sent' => $sent, 'recipients' => count($emails), ]; } }