388 lines
13 KiB
PHP
388 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Families;
|
|
|
|
use App\Models\Family;
|
|
use App\Models\Student;
|
|
use App\Models\StudentClass;
|
|
use App\Models\User;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class FamilyQueryService
|
|
{
|
|
public function __construct(private FamilyFinanceService $finance)
|
|
{
|
|
}
|
|
|
|
public function listStudentsForSelect(): array
|
|
{
|
|
return DB::table('students')
|
|
->select('id', DB::raw("CONCAT(lastname, ', ', firstname) AS name"))
|
|
->orderBy('lastname')
|
|
->orderBy('firstname')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
}
|
|
|
|
public function searchSuggestions(string $query, int $limit = 8): array
|
|
{
|
|
$query = trim($query);
|
|
if ($query === '') {
|
|
return [];
|
|
}
|
|
|
|
$qs = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $query) . '%';
|
|
|
|
$students = DB::table('students')
|
|
->select('id', 'firstname', 'lastname')
|
|
->whereRaw("CONCAT_WS(' ', firstname, lastname) LIKE ?", [$qs])
|
|
->orderBy('lastname')
|
|
->orderBy('firstname')
|
|
->limit($limit)
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
|
|
$guardians = DB::table('users')
|
|
->select('id', 'firstname', 'lastname', 'email', 'cellphone')
|
|
->where(function ($q) use ($qs) {
|
|
$q->whereRaw("CONCAT_WS(' ', firstname, lastname) LIKE ?", [$qs])
|
|
->orWhere('email', 'like', $qs)
|
|
->orWhere('cellphone', 'like', $qs);
|
|
})
|
|
->orderBy('lastname')
|
|
->orderBy('firstname')
|
|
->limit($limit)
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
|
|
$items = [];
|
|
foreach ($students as $s) {
|
|
$items[] = [
|
|
'type' => 'student',
|
|
'id' => (int) ($s['id'] ?? 0),
|
|
'label' => trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? '')),
|
|
'sub' => 'Student',
|
|
];
|
|
}
|
|
foreach ($guardians as $g) {
|
|
$items[] = [
|
|
'type' => 'guardian',
|
|
'id' => (int) ($g['id'] ?? 0),
|
|
'label' => trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? '')),
|
|
'sub' => trim(($g['email'] ?? '') . ' ' . ($g['cellphone'] ?? '')),
|
|
];
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
public function resolveStudentIdByGuardian(int $guardianId): ?int
|
|
{
|
|
if ($guardianId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$row = DB::table('family_guardians as fg')
|
|
->join('family_students as fs', 'fs.family_id', '=', 'fg.family_id')
|
|
->join('students as s', 's.id', '=', 'fs.student_id')
|
|
->where('fg.user_id', $guardianId)
|
|
->orderBy('s.lastname')
|
|
->orderBy('s.firstname')
|
|
->select('s.id as student_id')
|
|
->first();
|
|
if ($row && !empty($row->student_id)) {
|
|
return (int) $row->student_id;
|
|
}
|
|
|
|
$fallback = Student::query()
|
|
->select('id')
|
|
->where('parent_id', $guardianId)
|
|
->orderBy('lastname')
|
|
->orderBy('firstname')
|
|
->first();
|
|
|
|
return $fallback?->id ? (int) $fallback->id : null;
|
|
}
|
|
|
|
public function listFamiliesByStudent(int $studentId): array
|
|
{
|
|
return DB::table('family_students as fs')
|
|
->join('families as f', 'f.id', '=', 'fs.family_id')
|
|
->where('fs.student_id', $studentId)
|
|
->where('f.is_active', 1)
|
|
->orderByDesc('fs.is_primary_home')
|
|
->orderBy('f.household_name')
|
|
->select('f.*', 'fs.is_primary_home')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
}
|
|
|
|
public function listGuardiansByFamily(int $familyId): array
|
|
{
|
|
return DB::table('family_guardians as fg')
|
|
->join('users as u', 'u.id', '=', 'fg.user_id')
|
|
->where('fg.family_id', $familyId)
|
|
->orderByDesc('fg.is_primary')
|
|
->orderBy('u.lastname')
|
|
->orderBy('u.firstname')
|
|
->select(
|
|
'u.id as user_id',
|
|
'u.firstname',
|
|
'u.lastname',
|
|
'u.email',
|
|
'u.cellphone',
|
|
'u.address_street',
|
|
'u.city',
|
|
'u.state',
|
|
'u.zip',
|
|
'fg.relation',
|
|
'fg.is_primary',
|
|
'fg.receive_emails',
|
|
'fg.receive_sms'
|
|
)
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
}
|
|
|
|
public function adminIndexData(?int $studentId, ?int $guardianId, string $schoolYear): array
|
|
{
|
|
$studentId = (int) ($studentId ?? 0);
|
|
$guardianId = (int) ($guardianId ?? 0);
|
|
|
|
$resolvedStudentId = null;
|
|
if ($studentId <= 0 && $guardianId > 0) {
|
|
$resolvedStudentId = $this->resolveStudentIdByGuardian($guardianId);
|
|
if ($resolvedStudentId) {
|
|
$studentId = $resolvedStudentId;
|
|
}
|
|
}
|
|
|
|
$data = [
|
|
'student' => null,
|
|
'families' => [],
|
|
'guardians' => [],
|
|
'students' => $this->listStudentsForSelect(),
|
|
'searchStudents' => DB::table('students')
|
|
->select('id', 'firstname', 'lastname')
|
|
->orderBy('lastname')
|
|
->orderBy('firstname')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all(),
|
|
'searchGuardians' => DB::table('family_guardians as fg')
|
|
->join('users as u', 'u.id', '=', 'fg.user_id')
|
|
->distinct()
|
|
->select('u.id', 'u.firstname', 'u.lastname', 'u.email', 'u.cellphone')
|
|
->orderBy('u.lastname')
|
|
->orderBy('u.firstname')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all(),
|
|
'resolved_student_id' => $resolvedStudentId,
|
|
];
|
|
|
|
if ($studentId > 0) {
|
|
$data['student'] = DB::table('students')
|
|
->select('id', 'firstname', 'lastname')
|
|
->where('id', $studentId)
|
|
->first();
|
|
$data['student'] = $data['student'] ? (array) $data['student'] : null;
|
|
|
|
$families = DB::table('family_students as fs')
|
|
->join('families as f', 'f.id', '=', 'fs.family_id')
|
|
->where('fs.student_id', $studentId)
|
|
->orderByDesc('fs.is_primary_home')
|
|
->orderBy('f.household_name')
|
|
->select(
|
|
'f.id',
|
|
'f.family_code',
|
|
'f.household_name',
|
|
'f.address_line1',
|
|
'f.address_line2',
|
|
'f.city',
|
|
'f.state',
|
|
'f.postal_code',
|
|
'f.country',
|
|
'f.primary_phone',
|
|
'f.preferred_lang',
|
|
'f.preferred_contact_method',
|
|
'f.is_active',
|
|
'fs.is_primary_home'
|
|
)
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
|
|
foreach ($families as &$fam) {
|
|
$familyId = (int) ($fam['id'] ?? 0);
|
|
$fam['guardians'] = $this->listGuardiansByFamily($familyId);
|
|
$fam['students'] = $this->studentsForFamily($familyId, $schoolYear);
|
|
|
|
$parentIds = array_values(array_filter(array_map(
|
|
static fn ($g) => (int) ($g['user_id'] ?? 0),
|
|
$fam['guardians']
|
|
)));
|
|
$finance = $this->finance->loadFinancialsForParents($parentIds);
|
|
$fam['invoices'] = $finance['invoices'];
|
|
$fam['payments'] = $finance['payments'];
|
|
$fam['finance_summary'] = $finance['summary'];
|
|
$fam['invoice_map'] = $finance['invoice_map'];
|
|
}
|
|
unset($fam);
|
|
$data['families'] = $families;
|
|
|
|
if (!empty($families)) {
|
|
$familyId = (int) ($families[0]['id'] ?? 0);
|
|
$data['guardians'] = $familyId ? $this->listGuardiansByFamily($familyId) : [];
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function familyCard(?int $studentId, ?int $guardianId, ?int $familyId, string $schoolYear): ?array
|
|
{
|
|
$studentId = (int) ($studentId ?? 0);
|
|
$guardianId = (int) ($guardianId ?? 0);
|
|
$familyId = (int) ($familyId ?? 0);
|
|
|
|
if ($familyId <= 0) {
|
|
if ($studentId > 0) {
|
|
$row = DB::table('family_students as fs')
|
|
->join('families as f', 'f.id', '=', 'fs.family_id')
|
|
->where('fs.student_id', $studentId)
|
|
->orderByDesc('fs.is_primary_home')
|
|
->orderBy('f.household_name')
|
|
->select('f.id')
|
|
->first();
|
|
$familyId = $row?->id ? (int) $row->id : 0;
|
|
} elseif ($guardianId > 0) {
|
|
$row = DB::table('family_guardians as fg')
|
|
->join('families as f', 'f.id', '=', 'fg.family_id')
|
|
->where('fg.user_id', $guardianId)
|
|
->orderBy('f.household_name')
|
|
->select('f.id')
|
|
->first();
|
|
if ($row?->id) {
|
|
$familyId = (int) $row->id;
|
|
} else {
|
|
$fallback = DB::table('students as s')
|
|
->join('family_students as fs', 'fs.student_id', '=', 's.id')
|
|
->join('families as f', 'f.id', '=', 'fs.family_id')
|
|
->where('s.parent_id', $guardianId)
|
|
->orderByDesc('fs.is_primary_home')
|
|
->orderBy('f.household_name')
|
|
->select('f.id')
|
|
->first();
|
|
$familyId = $fallback?->id ? (int) $fallback->id : 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($familyId <= 0) {
|
|
return null;
|
|
}
|
|
|
|
$family = Family::query()
|
|
->select(
|
|
'id',
|
|
'family_code',
|
|
'household_name',
|
|
'address_line1',
|
|
'address_line2',
|
|
'city',
|
|
'state',
|
|
'postal_code',
|
|
'country',
|
|
'primary_phone',
|
|
'preferred_lang',
|
|
'preferred_contact_method',
|
|
'is_active'
|
|
)
|
|
->find($familyId);
|
|
|
|
if (!$family) {
|
|
return null;
|
|
}
|
|
|
|
$payload = $family->toArray();
|
|
$payload['guardians'] = $this->listGuardiansByFamily($familyId);
|
|
$payload['students'] = $this->studentsForFamily($familyId, $schoolYear);
|
|
$payload['emergency_contacts'] = $this->emergencyContactsForParents($payload['guardians']);
|
|
|
|
$parentIds = array_values(array_filter(array_map(
|
|
static fn ($g) => (int) ($g['user_id'] ?? 0),
|
|
$payload['guardians']
|
|
)));
|
|
$finance = $this->finance->loadFinancialsForParents($parentIds);
|
|
$payload['invoices'] = $finance['invoices'];
|
|
$payload['payments'] = $finance['payments'];
|
|
$payload['finance_summary'] = $finance['summary'];
|
|
$payload['invoice_map'] = $finance['invoice_map'];
|
|
|
|
return $payload;
|
|
}
|
|
|
|
private function studentsForFamily(int $familyId, string $schoolYear): array
|
|
{
|
|
$rows = DB::table('family_students as fs')
|
|
->join('students as s', 's.id', '=', 'fs.student_id')
|
|
->where('fs.family_id', $familyId)
|
|
->orderBy('s.lastname')
|
|
->orderBy('s.firstname')
|
|
->select('s.id', 's.firstname', 's.lastname')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
|
|
foreach ($rows as &$row) {
|
|
$sid = (int) ($row['id'] ?? 0);
|
|
$row['grade'] = $sid ? (string) (StudentClass::getClassSectionsByStudentId($sid, $schoolYear) ?? '') : '';
|
|
}
|
|
unset($row);
|
|
|
|
return $rows;
|
|
}
|
|
|
|
private function emergencyContactsForParents(array $guardians): array
|
|
{
|
|
$parentIds = array_values(array_filter(array_map(
|
|
static fn ($g) => (int) ($g['user_id'] ?? 0),
|
|
$guardians
|
|
)));
|
|
|
|
if (empty($parentIds)) {
|
|
return [];
|
|
}
|
|
|
|
$guardianMap = [];
|
|
foreach ($guardians as $g) {
|
|
$gid = (int) ($g['user_id'] ?? 0);
|
|
if ($gid > 0) {
|
|
$guardianMap[$gid] = trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? ''));
|
|
}
|
|
}
|
|
|
|
$rows = DB::table('emergency_contacts')
|
|
->select('id', 'parent_id', 'emergency_contact_name', 'relation', 'cellphone', 'email', 'school_year', 'semester', 'created_at', 'updated_at')
|
|
->whereIn('parent_id', $parentIds)
|
|
->orderBy('updated_at', 'DESC')
|
|
->get()
|
|
->map(fn ($r) => (array) $r)
|
|
->all();
|
|
|
|
foreach ($rows as &$row) {
|
|
$pid = (int) ($row['parent_id'] ?? 0);
|
|
$row['parent_label'] = $guardianMap[$pid] ?? ('Parent #' . $pid);
|
|
}
|
|
unset($row);
|
|
|
|
return $rows;
|
|
}
|
|
}
|