add projet
This commit is contained in:
+351
@@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\CommunicationLog;
|
||||
use App\Models\Communication;
|
||||
use App\Models\EmailTemplate;
|
||||
use App\Models\FamilyStudent;
|
||||
use App\Models\Student;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CommunicationController extends BaseApiController
|
||||
{
|
||||
protected Communication $communication;
|
||||
protected Student $student;
|
||||
protected FamilyStudent $familyStudent;
|
||||
protected EmailTemplate $template;
|
||||
protected CommunicationLog $log;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->communication = model(Communication::class);
|
||||
$this->student = model(Student::class);
|
||||
$this->familyStudent = model(FamilyStudent::class);
|
||||
$this->template = model(EmailTemplate::class);
|
||||
$this->log = model(CommunicationLog::class);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
|
||||
$perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 20)));
|
||||
$senderId = $this->request->getGet('sender_id');
|
||||
$receiverId = $this->request->getGet('receiver_id');
|
||||
|
||||
$query = $this->communication->newQuery();
|
||||
if ($senderId) {
|
||||
$query->where('sender_id', $senderId);
|
||||
}
|
||||
if ($receiverId) {
|
||||
$query->where('receiver_id', $receiverId);
|
||||
}
|
||||
$query->orderBy('created_at', 'DESC');
|
||||
|
||||
$result = $this->paginate($query, $page, $perPage);
|
||||
|
||||
return $this->success($result, 'Communications retrieved successfully');
|
||||
}
|
||||
|
||||
public function send()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$rules = [
|
||||
'sender_id' => 'required|integer',
|
||||
'receiver_id' => 'required|integer',
|
||||
'subject' => 'required|string|max_length[255]',
|
||||
'message' => 'required|string',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($data, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$communication = $this->communication->create([
|
||||
'sender_id' => $data['sender_id'],
|
||||
'receiver_id' => $data['receiver_id'],
|
||||
'subject' => $data['subject'],
|
||||
'message' => $data['message'],
|
||||
]);
|
||||
|
||||
return $this->respondCreated([
|
||||
'id' => $communication->id,
|
||||
'data' => $communication,
|
||||
], 'Message sent successfully');
|
||||
}
|
||||
|
||||
public function conversation()
|
||||
{
|
||||
$senderId = $this->request->getGet('sender_id');
|
||||
$receiverId = $this->request->getGet('receiver_id');
|
||||
|
||||
if (!$senderId || !$receiverId) {
|
||||
return $this->respondValidationError([
|
||||
'sender_id' => 'sender_id is required',
|
||||
'receiver_id' => 'receiver_id is required',
|
||||
]);
|
||||
}
|
||||
|
||||
$conversation = $this->communication->newQuery()
|
||||
->where(function ($query) use ($senderId, $receiverId) {
|
||||
$query->where('sender_id', $senderId)
|
||||
->where('receiver_id', $receiverId);
|
||||
})
|
||||
->orWhere(function ($query) use ($senderId, $receiverId) {
|
||||
$query->where('sender_id', $receiverId)
|
||||
->where('receiver_id', $senderId);
|
||||
})
|
||||
->orderBy('created_at', 'ASC')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
return $this->success($conversation, 'Conversation retrieved successfully');
|
||||
}
|
||||
|
||||
public function delete($id = null)
|
||||
{
|
||||
$message = $this->communication->find($id);
|
||||
if (!$message) {
|
||||
return $this->respondError('Message not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$message->delete();
|
||||
return $this->respondDeleted(null, 'Message deleted successfully');
|
||||
}
|
||||
|
||||
public function metadata()
|
||||
{
|
||||
$students = $this->student->newQuery()
|
||||
->selectRaw('id, firstname, lastname, COALESCE(registration_grade, \'\') AS grade')
|
||||
->orderBy('lastname', 'ASC')
|
||||
->orderBy('firstname', 'ASC')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$templates = $this->template->getActiveTemplates();
|
||||
|
||||
return $this->success([
|
||||
'students' => $students,
|
||||
'templates' => $templates,
|
||||
], 'Communication metadata retrieved');
|
||||
}
|
||||
|
||||
public function families(int $studentId)
|
||||
{
|
||||
$families = $this->familyStudent->getFamiliesForStudent($studentId);
|
||||
return $this->success(['data' => $families], 'Families retrieved');
|
||||
}
|
||||
|
||||
public function guardians(int $familyId)
|
||||
{
|
||||
$rows = DB::table('family_guardians as fg')
|
||||
->select('u.id AS user_id', 'u.firstname', 'u.lastname', 'u.email', 'fg.relation', 'fg.is_primary', 'fg.receive_emails', 'fg.receive_sms')
|
||||
->join('users as u', 'u.id', '=', 'fg.user_id')
|
||||
->where('fg.family_id', $familyId)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->toArray();
|
||||
|
||||
return $this->success(['data' => $rows], 'Guardians retrieved');
|
||||
}
|
||||
|
||||
public function preview()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$rules = [
|
||||
'template_key' => 'required|string',
|
||||
'student_id' => 'required|integer',
|
||||
'family_id' => 'required|integer',
|
||||
'vars' => 'permit_empty',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($data, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$template = $this->template->findByKey((string) ($data['template_key'] ?? ''));
|
||||
if (!$template) {
|
||||
return $this->respondError('Template not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$student = $this->student->find($data['student_id']);
|
||||
if (!$student) {
|
||||
return $this->respondError('Student not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$guardianRows = DB::table('family_guardians as fg')
|
||||
->select('u.firstname', 'u.lastname')
|
||||
->join('users as u', 'u.id', '=', 'fg.user_id')
|
||||
->where('fg.family_id', $data['family_id'])
|
||||
->where('fg.receive_emails', 1)
|
||||
->orderByDesc('fg.is_primary')
|
||||
->orderBy('u.lastname')
|
||||
->orderBy('u.firstname')
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->toArray();
|
||||
|
||||
$salutation = 'Parent/Guardian';
|
||||
if (!empty($guardianRows)) {
|
||||
$names = array_map(fn ($row) => trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')), $guardianRows);
|
||||
$names = array_filter($names, fn ($name) => $name !== '');
|
||||
if (!empty($names)) {
|
||||
$salutation = implode(' & ', $names);
|
||||
}
|
||||
}
|
||||
|
||||
$varsPost = $data['vars'] ?? null;
|
||||
$vars = $this->normalizeVars($varsPost);
|
||||
|
||||
$autoVars = [
|
||||
'student_fullname' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')),
|
||||
'student_grade' => $student['grade'] ?? $student['registration_grade'] ?? '',
|
||||
'parent_salutation'=> $salutation,
|
||||
'date' => local_date(utc_now(), 'Y-m-d'),
|
||||
'school_name' => 'Al Rahma Sunday School',
|
||||
'teacher_name' => (string) (session('display_name') ?? 'Teacher'),
|
||||
];
|
||||
|
||||
$context = array_merge($autoVars, $vars);
|
||||
$subject = $this->renderTwig($template['subject'] ?? '', $context);
|
||||
$body = nl2br($this->renderTwig($template['body'] ?? '', $context));
|
||||
|
||||
return $this->success([
|
||||
'subject' => $subject,
|
||||
'html' => $body,
|
||||
], 'Template preview generated');
|
||||
}
|
||||
|
||||
public function sendEmail()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$rules = [
|
||||
'student_id' => 'required|integer',
|
||||
'family_id' => 'required|integer',
|
||||
'template_key' => 'required|string',
|
||||
'subject' => 'required|string',
|
||||
'body' => 'required|string',
|
||||
'recipients' => 'required',
|
||||
];
|
||||
|
||||
$errors = $this->validateRequest($data, $rules);
|
||||
if (!empty($errors)) {
|
||||
return $this->respondValidationError($errors);
|
||||
}
|
||||
|
||||
$student = $this->student->find($data['student_id']);
|
||||
if (!$student) {
|
||||
return $this->respondError('Student not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
$recipients = $this->normalizeRecipients($data['recipients']);
|
||||
$cc = $this->normalizeRecipients($data['cc'] ?? null);
|
||||
$bcc = $this->normalizeRecipients($data['bcc'] ?? null);
|
||||
|
||||
if (empty($recipients)) {
|
||||
return $this->respondError('At least one recipient is required');
|
||||
}
|
||||
|
||||
$bodyHtml = (string) ($data['body'] ?? '');
|
||||
$subject = (string) ($data['subject'] ?? '');
|
||||
$fromAddress = config('mail.from.address', 'no-reply@alrahmaisgl.org');
|
||||
$fromName = config('mail.from.name', config('app.name', 'Al Rahma'));
|
||||
|
||||
$status = 'failed';
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
Mail::send([], [], function (Message $message) use ($fromAddress, $fromName, $subject, $bodyHtml, $recipients, $cc, $bcc) {
|
||||
$message->from($fromAddress, $fromName);
|
||||
$message->to($recipients);
|
||||
|
||||
if (!empty($cc)) {
|
||||
$message->cc($cc);
|
||||
}
|
||||
if (!empty($bcc)) {
|
||||
$message->bcc($bcc);
|
||||
}
|
||||
|
||||
$message->subject($subject);
|
||||
$message->setBody($bodyHtml, 'text/html');
|
||||
$message->addPart(strip_tags($bodyHtml), 'text/plain');
|
||||
});
|
||||
$status = 'sent';
|
||||
} catch (\Throwable $t) {
|
||||
$error = $t->getMessage();
|
||||
}
|
||||
|
||||
$studentName = trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? ''));
|
||||
|
||||
$this->log->insert([
|
||||
'student_id' => $data['student_id'],
|
||||
'family_id' => $data['family_id'],
|
||||
'student_name' => $studentName,
|
||||
'template_key' => $data['template_key'],
|
||||
'subject' => $subject,
|
||||
'body' => $bodyHtml,
|
||||
'recipients' => json_encode(array_values($recipients)),
|
||||
'cc' => json_encode(array_values($cc)),
|
||||
'bcc' => json_encode(array_values($bcc)),
|
||||
'status' => $status,
|
||||
'error_message' => $error,
|
||||
'sent_by' => (int) (session('user_id') ?? 0),
|
||||
'metadata' => null,
|
||||
]);
|
||||
|
||||
if ($status === 'sent') {
|
||||
return $this->success(['status' => 'sent'], 'Email sent successfully');
|
||||
}
|
||||
|
||||
return $this->error('Failed to send email: ' . ($error ?? 'Unknown error'), Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private function normalizeVars($value): array
|
||||
{
|
||||
if (is_string($value)) {
|
||||
$decoded = json_decode($value, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
return $decoded;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function normalizeRecipients($input): array
|
||||
{
|
||||
$values = [];
|
||||
if (is_string($input)) {
|
||||
$decoded = json_decode($input, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
$values = $decoded;
|
||||
} elseif ($input !== '') {
|
||||
$values = [$input];
|
||||
}
|
||||
} elseif (is_array($input)) {
|
||||
$values = $input;
|
||||
}
|
||||
|
||||
$sanitized = array_values(array_filter(array_map(fn ($item) => trim((string) $item), $values)));
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
private function renderTwig(string $template, array $vars): string
|
||||
{
|
||||
return preg_replace_callback('/\{\{\s*([a-zA-Z0-9_\.]+)\s*\}\}/', function ($match) use ($vars) {
|
||||
$key = $match[1];
|
||||
return htmlspecialchars((string) ($vars[$key] ?? ''), ENT_QUOTES, 'UTF-8');
|
||||
}, $template);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user