add controllers, servoices
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
use App\Models\FamilyStudent;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CommunicationFamilyService
|
||||
{
|
||||
public function familiesForStudent(int $studentId): array
|
||||
{
|
||||
return FamilyStudent::getFamiliesForStudent($studentId);
|
||||
}
|
||||
|
||||
public function guardiansForFamily(int $familyId): array
|
||||
{
|
||||
return DB::table('family_guardians as fg')
|
||||
->join('users as u', 'u.id', '=', 'fg.user_id')
|
||||
->where('fg.family_id', $familyId)
|
||||
->select(
|
||||
'u.id as user_id',
|
||||
'u.firstname',
|
||||
'u.lastname',
|
||||
'u.email',
|
||||
'fg.relation',
|
||||
'fg.is_primary',
|
||||
'fg.receive_emails',
|
||||
'fg.receive_sms'
|
||||
)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->all();
|
||||
}
|
||||
|
||||
public function guardianSalutation(int $familyId): string
|
||||
{
|
||||
$rows = DB::table('family_guardians as fg')
|
||||
->join('users as u', 'u.id', '=', 'fg.user_id')
|
||||
->where('fg.family_id', $familyId)
|
||||
->where('fg.receive_emails', 1)
|
||||
->orderByDesc('fg.is_primary')
|
||||
->orderBy('u.lastname')
|
||||
->orderBy('u.firstname')
|
||||
->select('u.firstname', 'u.lastname')
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->all();
|
||||
|
||||
if (empty($rows)) {
|
||||
return 'Parent/Guardian';
|
||||
}
|
||||
|
||||
$names = array_map(static function ($row) {
|
||||
return trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? ''));
|
||||
}, $rows);
|
||||
|
||||
return implode(' & ', $names);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
use App\Models\CommunicationLog;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CommunicationLogService
|
||||
{
|
||||
public function log(array $payload): void
|
||||
{
|
||||
if (!Schema::hasTable('communication_logs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
CommunicationLog::query()->create($payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
class CommunicationPreviewService
|
||||
{
|
||||
private CommunicationTemplateService $templates;
|
||||
private CommunicationStudentService $students;
|
||||
private CommunicationFamilyService $families;
|
||||
|
||||
public function __construct(
|
||||
CommunicationTemplateService $templates,
|
||||
CommunicationStudentService $students,
|
||||
CommunicationFamilyService $families
|
||||
) {
|
||||
$this->templates = $templates;
|
||||
$this->students = $students;
|
||||
$this->families = $families;
|
||||
}
|
||||
|
||||
public function buildPreview(
|
||||
string $templateKey,
|
||||
int $studentId,
|
||||
int $familyId,
|
||||
array $vars,
|
||||
string $teacherName
|
||||
): array {
|
||||
$template = $this->templates->findTemplateByKey($templateKey);
|
||||
if (!$template) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'status' => 404,
|
||||
'message' => 'Template not found',
|
||||
];
|
||||
}
|
||||
|
||||
$student = $this->students->getStudentBasic($studentId);
|
||||
if (!$student) {
|
||||
return [
|
||||
'ok' => false,
|
||||
'status' => 404,
|
||||
'message' => 'Student not found',
|
||||
];
|
||||
}
|
||||
|
||||
$salutation = $this->families->guardianSalutation($familyId);
|
||||
|
||||
$autoVars = [
|
||||
'student_fullname' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')),
|
||||
'student_grade' => $student['grade'] ?? '',
|
||||
'parent_salutation' => $salutation,
|
||||
'date' => $this->formatLocalDate('Y-m-d'),
|
||||
'school_name' => 'Al Rahma Sunday School',
|
||||
'teacher_name' => $teacherName !== '' ? $teacherName : 'Teacher',
|
||||
];
|
||||
|
||||
$allVars = array_merge($autoVars, $vars);
|
||||
|
||||
$subject = $this->renderTwig($template['subject'], $allVars);
|
||||
$body = $this->renderTwig($template['body'], $allVars);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'subject' => $subject,
|
||||
'html' => nl2br($body),
|
||||
];
|
||||
}
|
||||
|
||||
private function renderTwig(string $template, array $vars): string
|
||||
{
|
||||
return (string) preg_replace_callback('/\\{\\{\\s*([a-zA-Z0-9_\\.]+)\\s*\\}\\}/', function ($m) use ($vars) {
|
||||
$key = $m[1];
|
||||
return htmlspecialchars((string) ($vars[$key] ?? ''), ENT_QUOTES, 'UTF-8');
|
||||
}, $template);
|
||||
}
|
||||
|
||||
private function formatLocalDate(string $format): string
|
||||
{
|
||||
if (function_exists('local_date') && function_exists('utc_now')) {
|
||||
return (string) local_date(utc_now(), $format);
|
||||
}
|
||||
return now()->format($format);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
use App\Services\EmailService;
|
||||
|
||||
class CommunicationSendService
|
||||
{
|
||||
private EmailService $mailer;
|
||||
private CommunicationLogService $logService;
|
||||
|
||||
public function __construct(EmailService $mailer, CommunicationLogService $logService)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
$this->logService = $logService;
|
||||
}
|
||||
|
||||
public function send(array $payload): array
|
||||
{
|
||||
$recipients = $this->normalizeEmails($payload['recipients'] ?? []);
|
||||
$cc = $this->normalizeEmails($payload['cc'] ?? []);
|
||||
$bcc = $this->normalizeEmails($payload['bcc'] ?? []);
|
||||
|
||||
if (empty($recipients)) {
|
||||
$this->logService->log([
|
||||
'student_id' => (int) $payload['student_id'],
|
||||
'family_id' => (int) $payload['family_id'],
|
||||
'student_name' => (string) $payload['student_name'],
|
||||
'template_key' => (string) $payload['template_key'],
|
||||
'subject' => (string) $payload['subject'],
|
||||
'body' => (string) $payload['body'],
|
||||
'recipients' => [],
|
||||
'cc' => array_values($cc),
|
||||
'bcc' => array_values($bcc),
|
||||
'status' => 'failed',
|
||||
'error_message' => 'No recipients provided',
|
||||
'sent_by' => (int) ($payload['sent_by'] ?? 0),
|
||||
'metadata' => null,
|
||||
]);
|
||||
|
||||
return [
|
||||
'ok' => false,
|
||||
'error' => 'No recipients provided',
|
||||
];
|
||||
}
|
||||
|
||||
$sendOk = false;
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
$sendOk = $this->mailer->send(
|
||||
$recipients,
|
||||
(string) $payload['subject'],
|
||||
(string) $payload['body'],
|
||||
'general',
|
||||
$cc,
|
||||
$bcc
|
||||
);
|
||||
} catch (\Throwable $t) {
|
||||
$error = $t->getMessage();
|
||||
$sendOk = false;
|
||||
}
|
||||
|
||||
if (!$sendOk && $error === null) {
|
||||
$error = 'Mailer returned false';
|
||||
}
|
||||
|
||||
$this->logService->log([
|
||||
'student_id' => (int) $payload['student_id'],
|
||||
'family_id' => (int) $payload['family_id'],
|
||||
'student_name' => (string) $payload['student_name'],
|
||||
'template_key' => (string) $payload['template_key'],
|
||||
'subject' => (string) $payload['subject'],
|
||||
'body' => (string) $payload['body'],
|
||||
'recipients' => array_values($recipients),
|
||||
'cc' => array_values($cc),
|
||||
'bcc' => array_values($bcc),
|
||||
'status' => $sendOk ? 'sent' : 'failed',
|
||||
'error_message' => $error,
|
||||
'sent_by' => (int) ($payload['sent_by'] ?? 0),
|
||||
'metadata' => null,
|
||||
]);
|
||||
|
||||
return [
|
||||
'ok' => $sendOk,
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeEmails(array $values): array
|
||||
{
|
||||
$out = array_values(array_unique(array_filter(array_map('strval', $values))));
|
||||
return array_values(array_filter($out, static function ($email) {
|
||||
return $email !== '';
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CommunicationStudentService
|
||||
{
|
||||
public function listStudents(): array
|
||||
{
|
||||
return DB::table('students')
|
||||
->select('id', 'firstname', 'lastname', 'registration_grade')
|
||||
->orderBy('lastname', 'asc')
|
||||
->orderBy('firstname', 'asc')
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->all();
|
||||
}
|
||||
|
||||
public function getStudentBasic(int $studentId): ?array
|
||||
{
|
||||
$row = DB::table('students as s')
|
||||
->leftJoin('student_class as sc', 'sc.student_id', '=', 's.id')
|
||||
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id')
|
||||
->select(
|
||||
's.id',
|
||||
's.firstname',
|
||||
's.lastname',
|
||||
's.registration_grade',
|
||||
'cs.class_section_name as class_section_name'
|
||||
)
|
||||
->where('s.id', $studentId)
|
||||
->orderBy('sc.id', 'desc')
|
||||
->first();
|
||||
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = (array) $row;
|
||||
$grade = $row['registration_grade'] ?? '';
|
||||
if (!$grade && !empty($row['class_section_name'])) {
|
||||
$grade = $row['class_section_name'];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $row['id'],
|
||||
'firstname' => (string) ($row['firstname'] ?? ''),
|
||||
'lastname' => (string) ($row['lastname'] ?? ''),
|
||||
'grade' => (string) ($grade ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Communication;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CommunicationTemplateService
|
||||
{
|
||||
public function listActiveTemplates(): array
|
||||
{
|
||||
[$keyColumn, $bodyColumn] = $this->resolveTemplateColumns();
|
||||
|
||||
return DB::table('email_templates')
|
||||
->where('is_active', 1)
|
||||
->orderBy($keyColumn, 'asc')
|
||||
->get()
|
||||
->map(function ($row) use ($keyColumn, $bodyColumn) {
|
||||
$row = (array) $row;
|
||||
return [
|
||||
'template_key' => (string) ($row[$keyColumn] ?? ''),
|
||||
'subject' => (string) ($row['subject'] ?? ''),
|
||||
'body' => (string) ($row[$bodyColumn] ?? ''),
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
public function findTemplateByKey(string $key): ?array
|
||||
{
|
||||
[$keyColumn, $bodyColumn] = $this->resolveTemplateColumns();
|
||||
|
||||
$row = DB::table('email_templates')
|
||||
->where($keyColumn, $key)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
|
||||
if (!$row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = (array) $row;
|
||||
|
||||
return [
|
||||
'template_key' => (string) ($row[$keyColumn] ?? ''),
|
||||
'subject' => (string) ($row['subject'] ?? ''),
|
||||
'body' => (string) ($row[$bodyColumn] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
private function resolveTemplateColumns(): array
|
||||
{
|
||||
$keyColumn = Schema::hasColumn('email_templates', 'template_key') ? 'template_key' : 'code';
|
||||
$bodyColumn = Schema::hasColumn('email_templates', 'body') ? 'body' : 'body_html';
|
||||
return [$keyColumn, $bodyColumn];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user