940afe9319
API CI/CD / Validate (composer + pint) (push) Successful in 2m7s
API CI/CD / Test (PHPUnit) (push) Failing after 2m23s
API CI/CD / Build frontend assets (push) Successful in 2m18s
API CI/CD / Security audit (push) Successful in 31s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
274 lines
11 KiB
PHP
274 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Whatsapp;
|
|
|
|
use App\Models\WhatsappGroupMembership;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class WhatsappContactService
|
|
{
|
|
public function __construct(private WhatsappContextService $context) {}
|
|
|
|
public function listParentContacts(?string $schoolYear, ?string $semester): array
|
|
{
|
|
$schoolYear = $this->normalizeFilter($schoolYear);
|
|
$semester = $this->normalizeFilter($semester);
|
|
|
|
$builder = DB::table('parents as sp');
|
|
$builder->select([
|
|
'sp.id AS parents_row_id',
|
|
'sp.firstparent_id AS firstparent_id',
|
|
'sp.secondparent_firstname AS sp_firstname',
|
|
'sp.secondparent_lastname AS sp_lastname',
|
|
'sp.secondparent_email AS sp_email',
|
|
'sp.secondparent_phone AS sp_phone',
|
|
'sp.school_year AS sp_school_year',
|
|
'sp.semester AS sp_semester',
|
|
'u.id AS u_id',
|
|
'u.firstname AS u_firstname',
|
|
'u.lastname AS u_lastname',
|
|
'u.email AS u_email',
|
|
'u.cellphone AS u_phone',
|
|
'u.school_year AS u_school_year',
|
|
'u.semester AS u_semester',
|
|
]);
|
|
$builder->leftJoin('users as u', 'u.id', '=', 'sp.firstparent_id');
|
|
|
|
if ($schoolYear !== '') {
|
|
$builder->where('sp.school_year', $schoolYear);
|
|
}
|
|
if ($semester !== '') {
|
|
$builder->where('sp.semester', $semester);
|
|
}
|
|
|
|
$rows = $builder->get()->all();
|
|
|
|
$contacts = [];
|
|
foreach ($rows as $row) {
|
|
$r = (array) $row;
|
|
if (! empty($r['u_id'])) {
|
|
$contacts[] = [
|
|
'firstname' => (string) ($r['u_firstname'] ?? ''),
|
|
'lastname' => (string) ($r['u_lastname'] ?? ''),
|
|
'email' => (string) ($r['u_email'] ?? ''),
|
|
'phone' => (string) ($r['u_phone'] ?? ''),
|
|
'type' => 'Primary Parent',
|
|
];
|
|
}
|
|
|
|
$hasSecond = ! empty($r['sp_firstname']) || ! empty($r['sp_lastname']) || ! empty($r['sp_email']) || ! empty($r['sp_phone']);
|
|
if ($hasSecond) {
|
|
$contacts[] = [
|
|
'firstname' => (string) ($r['sp_firstname'] ?? ''),
|
|
'lastname' => (string) ($r['sp_lastname'] ?? ''),
|
|
'email' => (string) ($r['sp_email'] ?? ''),
|
|
'phone' => (string) ($r['sp_phone'] ?? ''),
|
|
'type' => 'Second Parent',
|
|
];
|
|
}
|
|
}
|
|
|
|
$contacts = array_values(array_filter($contacts, function ($c) {
|
|
$name = trim(($c['firstname'] ?? '').($c['lastname'] ?? ''));
|
|
$contact = trim(($c['phone'] ?? '').($c['email'] ?? ''));
|
|
|
|
return $name !== '' || $contact !== '';
|
|
}));
|
|
|
|
usort($contacts, function ($a, $b) {
|
|
$la = strtolower($a['lastname'] ?? '');
|
|
$lb = strtolower($b['lastname'] ?? '');
|
|
if ($la !== $lb) {
|
|
return $la <=> $lb;
|
|
}
|
|
$fa = strtolower($a['firstname'] ?? '');
|
|
$fb = strtolower($b['firstname'] ?? '');
|
|
if ($fa !== $fb) {
|
|
return $fa <=> $fb;
|
|
}
|
|
$ta = ($a['type'] ?? '') === 'Primary Parent' ? 0 : 1;
|
|
$tb = ($b['type'] ?? '') === 'Primary Parent' ? 0 : 1;
|
|
|
|
return $ta <=> $tb;
|
|
});
|
|
|
|
return $contacts;
|
|
}
|
|
|
|
public function listParentContactsByClass(?string $schoolYear, ?string $semester): array
|
|
{
|
|
$schoolYear = $this->normalizeFilter($schoolYear);
|
|
$semester = $this->normalizeFilter($semester);
|
|
|
|
$sections = DB::table('student_class as sc')
|
|
->select('sc.class_section_id', DB::raw('MAX(sc.school_year) AS school_year'))
|
|
->when($schoolYear !== '', fn ($q) => $q->where('sc.school_year', $schoolYear))
|
|
->groupBy('sc.class_section_id')
|
|
->orderBy('sc.class_section_id', 'ASC')
|
|
->get()
|
|
->all();
|
|
|
|
$sectionIds = array_values(array_filter(array_unique(array_map(
|
|
fn ($r) => (int) ($r->class_section_id ?? 0),
|
|
$sections
|
|
))));
|
|
|
|
$namesById = [];
|
|
if (! empty($sectionIds)) {
|
|
$nameRows = DB::table('classSection')
|
|
->select('class_section_id', 'class_section_name')
|
|
->whereIn('class_section_id', $sectionIds)
|
|
->get()
|
|
->all();
|
|
|
|
foreach ($nameRows as $row) {
|
|
$namesById[(int) $row->class_section_id] = (string) ($row->class_section_name ?? '');
|
|
}
|
|
}
|
|
|
|
$classSections = [];
|
|
foreach ($sections as $s) {
|
|
$sid = (int) ($s->class_section_id ?? 0);
|
|
if (! $sid) {
|
|
continue;
|
|
}
|
|
|
|
$classSections[$sid] = [
|
|
'class_section_id' => $sid,
|
|
'class_section_name' => $namesById[$sid] ?? ('Section '.$sid),
|
|
'semester' => $semester,
|
|
'school_year' => (string) ($s->school_year ?? ''),
|
|
'description' => '',
|
|
'main_teachers' => [],
|
|
'teacher_assistants' => [],
|
|
'parents' => [],
|
|
];
|
|
}
|
|
|
|
$rows = DB::table('student_class as sc')
|
|
->select([
|
|
'sc.class_section_id',
|
|
'u.id as primary_id',
|
|
'u.firstname as primary_firstname',
|
|
'u.lastname as primary_lastname',
|
|
'u.email as primary_email',
|
|
'u.cellphone as primary_phone',
|
|
'sp.id as second_id',
|
|
'sp.secondparent_firstname as second_firstname',
|
|
'sp.secondparent_lastname as second_lastname',
|
|
'sp.secondparent_email as second_email',
|
|
'sp.secondparent_phone as second_phone',
|
|
])
|
|
->join('students as s', 's.id', '=', 'sc.student_id')
|
|
->join('users as u', 'u.id', '=', 's.parent_id')
|
|
->leftJoin('parents as sp', function ($join) {
|
|
$join->on('sp.firstparent_id', '=', 'u.id')
|
|
->on('sp.school_year', '=', 'sc.school_year');
|
|
})
|
|
->when($schoolYear !== '', fn ($q) => $q->where('sc.school_year', $schoolYear))
|
|
->orderBy('sc.class_section_id', 'ASC')
|
|
->orderBy('u.lastname', 'ASC')
|
|
->orderBy('u.firstname', 'ASC')
|
|
->get()
|
|
->all();
|
|
|
|
$seen = [];
|
|
foreach ($rows as $row) {
|
|
$r = (array) $row;
|
|
$sid = (int) ($r['class_section_id'] ?? 0);
|
|
$pid = (int) ($r['primary_id'] ?? 0);
|
|
if (! $sid || ! $pid || ! isset($classSections[$sid])) {
|
|
continue;
|
|
}
|
|
|
|
$key = $sid.':'.$pid;
|
|
if (isset($seen[$key])) {
|
|
continue;
|
|
}
|
|
$seen[$key] = true;
|
|
|
|
$classSections[$sid]['parents'][] = [
|
|
'class_section_id' => $sid,
|
|
'primary_id' => $pid,
|
|
'primary_name' => trim(($r['primary_lastname'] ?? '').', '.($r['primary_firstname'] ?? '')),
|
|
'primary_phone' => (string) ($r['primary_phone'] ?? ''),
|
|
'primary_email' => (string) ($r['primary_email'] ?? ''),
|
|
'second_id' => (int) ($r['second_id'] ?? 0),
|
|
'second_name' => trim(($r['second_lastname'] ?? '').', '.($r['second_firstname'] ?? '')),
|
|
'second_phone' => (string) ($r['second_phone'] ?? ''),
|
|
'second_email' => (string) ($r['second_email'] ?? ''),
|
|
'primary_member' => null,
|
|
'second_member' => null,
|
|
];
|
|
}
|
|
|
|
try {
|
|
$sectionIds = array_keys($classSections);
|
|
$membershipMap = WhatsappGroupMembership::getBySectionsAndTerm($sectionIds, $schoolYear, $semester);
|
|
foreach ($classSections as $sid => &$section) {
|
|
foreach ($section['parents'] as &$parent) {
|
|
$key = sprintf('%d:primary:%d', (int) $parent['class_section_id'], (int) $parent['primary_id']);
|
|
if (isset($membershipMap[$key])) {
|
|
$parent['primary_member'] = (int) ($membershipMap[$key]['is_member'] ?? 0);
|
|
}
|
|
|
|
$secondId = (int) ($parent['second_id'] ?? 0);
|
|
if ($secondId > 0) {
|
|
$key2 = sprintf('%d:second:%d', (int) $parent['class_section_id'], $secondId);
|
|
if (isset($membershipMap[$key2])) {
|
|
$parent['second_member'] = (int) ($membershipMap[$key2]['is_member'] ?? 0);
|
|
}
|
|
}
|
|
}
|
|
unset($parent);
|
|
}
|
|
unset($section);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('WhatsApp membership lookup failed: '.$e->getMessage());
|
|
}
|
|
|
|
try {
|
|
$teachers = DB::table('teacher_class as tc')
|
|
->select('tc.class_section_id', 'tc.position', 'u.firstname', 'u.lastname')
|
|
->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
|
|
->when($schoolYear !== '', fn ($q) => $q->where('tc.school_year', $schoolYear))
|
|
->get()
|
|
->all();
|
|
|
|
foreach ($teachers as $row) {
|
|
$r = (array) $row;
|
|
$sid = (int) ($r['class_section_id'] ?? 0);
|
|
if (! isset($classSections[$sid])) {
|
|
continue;
|
|
}
|
|
$name = trim(($r['lastname'] ?? '').', '.($r['firstname'] ?? ''));
|
|
$pos = strtolower((string) ($r['position'] ?? 'main'));
|
|
if ($pos === 'ta' || $pos === 'assistant') {
|
|
$classSections[$sid]['teacher_assistants'][] = $name;
|
|
} else {
|
|
$classSections[$sid]['main_teachers'][] = $name;
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('WhatsApp teacher list lookup failed: '.$e->getMessage());
|
|
}
|
|
|
|
foreach ($classSections as &$section) {
|
|
$section['main_teachers'] = array_values(array_filter($section['main_teachers'] ?? []));
|
|
$section['teacher_assistants'] = array_values(array_filter($section['teacher_assistants'] ?? []));
|
|
$section['parents'] = array_values($section['parents'] ?? []);
|
|
}
|
|
unset($section);
|
|
|
|
return array_values($classSections);
|
|
}
|
|
|
|
private function normalizeFilter(?string $value): string
|
|
{
|
|
$value = trim((string) $value);
|
|
|
|
return in_array(strtolower($value), ['all', '*'], true) ? '' : $value;
|
|
}
|
|
}
|