add family logic
This commit is contained in:
@@ -0,0 +1,387 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user