Files
alrahma_sunday_school_api/app/Services/Families/ParentProfileAdminQueryService.php
T
root 031e499819
API CI/CD / Validate (composer + pint) (push) Successful in 3m10s
API CI/CD / Test (PHPUnit) (push) Failing after 6m49s
API CI/CD / Build frontend assets (push) Successful in 1m3s
API CI/CD / Security audit (push) Failing after 1m0s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
fix apply the plan docs/alrahma_api_fix_plan_school_year_only_v7
2026-07-07 01:52:29 -04:00

268 lines
11 KiB
PHP

<?php
namespace App\Services\Families;
use App\Models\StudentClass;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
class ParentProfileAdminQueryService
{
public function __construct(private FamilyFinanceService $finance) {}
public function index(string $schoolYear, string $search = '', int $perPage = 25): LengthAwarePaginator
{
$query = DB::table('users as u')
->select(
'u.id',
'u.firstname',
'u.lastname',
'u.email',
'u.cellphone',
'u.status',
DB::raw('COUNT(DISTINCT fg.family_id) as families_count'),
DB::raw('COUNT(DISTINCT s.id) as selected_year_students_count'),
DB::raw('COALESCE(SUM(DISTINCT i.balance), 0) as balance')
)
->leftJoin('family_guardians as fg', 'fg.user_id', '=', 'u.id')
->leftJoin('family_students as fs', 'fs.family_id', '=', 'fg.family_id')
->leftJoin('students as s', function ($join) use ($schoolYear) {
$join->on('s.id', '=', 'fs.student_id')
->where('s.school_year', '=', $schoolYear);
})
->leftJoin('invoices as i', function ($join) use ($schoolYear) {
$join->on('i.parent_id', '=', 'u.id')
->where('i.school_year', '=', $schoolYear);
})
->where($this->relevantParentConstraint($schoolYear))
->groupBy('u.id', 'u.firstname', 'u.lastname', 'u.email', 'u.cellphone', 'u.status')
->orderBy('u.lastname')
->orderBy('u.firstname');
if ($search !== '') {
$needle = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $search).'%';
$query->where(function ($inner) use ($needle) {
$inner->whereRaw("CONCAT_WS(' ', u.firstname, u.lastname) LIKE ?", [$needle])
->orWhere('u.email', 'like', $needle)
->orWhere('u.cellphone', 'like', $needle)
->orWhereExists(function ($exists) use ($needle) {
$exists->selectRaw('1')
->from('family_guardians as sfg')
->join('family_students as sfs', 'sfs.family_id', '=', 'sfg.family_id')
->join('students as ss', 'ss.id', '=', 'sfs.student_id')
->whereColumn('sfg.user_id', 'u.id')
->where(function ($student) use ($needle) {
$student->whereRaw("CONCAT_WS(' ', ss.firstname, ss.lastname) LIKE ?", [$needle]);
});
});
});
}
return $query->paginate(max(1, min($perPage, 100)));
}
public function search(string $schoolYear, string $search, int $limit = 10): array
{
if (trim($search) === '') {
return [];
}
return $this->index($schoolYear, $search, $limit)
->getCollection()
->map(fn ($row) => $this->parentSummary((array) $row, $schoolYear))
->all();
}
public function show(int $parentId, string $schoolYear): ?array
{
$parent = DB::table('users')
->select('id', 'firstname', 'lastname', 'email', 'cellphone', 'address_street', 'city', 'state', 'zip', 'status')
->where('id', $parentId)
->first();
if (! $parent) {
return null;
}
$guardians = [
[
'user_id' => $parentId,
'firstname' => $parent->firstname,
'lastname' => $parent->lastname,
],
];
$finance = $this->finance->loadFinancialsForParents([$parentId], $schoolYear);
return [
'parent' => (array) $parent,
'families' => $this->familiesForParent($parentId, $schoolYear),
'students' => $this->studentsForParent($parentId, $schoolYear),
'emergency_contacts' => $this->emergencyContactsForParents($guardians, $schoolYear),
'invoices' => $finance['invoices'],
'payments' => $finance['payments'],
'finance_summary' => $finance['summary'],
];
}
public function updateParent(int $parentId, array $payload): ?array
{
$allowed = array_intersect_key($payload, array_flip([
'firstname',
'lastname',
'email',
'cellphone',
'address_street',
'city',
'state',
'zip',
'status',
]));
if ($allowed !== []) {
$allowed['updated_at'] = now();
DB::table('users')->where('id', $parentId)->update($allowed);
}
$parent = DB::table('users')->where('id', $parentId)->first();
return $parent ? (array) $parent : null;
}
public function parentSummary(array $row, string $schoolYear): array
{
$balance = (float) ($row['balance'] ?? 0);
$primaryFamily = DB::table('family_guardians as fg')
->join('families as f', 'f.id', '=', 'fg.family_id')
->where('fg.user_id', (int) ($row['id'] ?? 0))
->orderByDesc('fg.is_primary')
->orderBy('f.household_name')
->select('f.id', 'f.household_name')
->first();
return [
'id' => (int) ($row['id'] ?? 0),
'firstname' => $row['firstname'] ?? null,
'lastname' => $row['lastname'] ?? null,
'email' => $row['email'] ?? null,
'cellphone' => $row['cellphone'] ?? null,
'is_active' => strtolower((string) ($row['status'] ?? '')) === 'active',
'families_count' => (int) ($row['families_count'] ?? 0),
'students_count' => (int) ($row['selected_year_students_count'] ?? 0),
'selected_year_students_count' => (int) ($row['selected_year_students_count'] ?? 0),
'balance' => $balance,
'positive_unpaid_balance' => max(0, $balance),
'credit_balance' => $balance < 0 ? abs($balance) : 0.0,
'primary_family' => $primaryFamily ? (array) $primaryFamily : null,
'school_year' => $schoolYear,
];
}
private function relevantParentConstraint(string $schoolYear): callable
{
return function ($query) use ($schoolYear) {
$query->whereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('family_guardians as rfg')
->join('family_students as rfs', 'rfs.family_id', '=', 'rfg.family_id')
->join('students as rs', 'rs.id', '=', 'rfs.student_id')
->whereColumn('rfg.user_id', 'u.id')
->where('rs.school_year', $schoolYear);
})->orWhereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('students as direct_students')
->whereColumn('direct_students.parent_id', 'u.id')
->where('direct_students.school_year', $schoolYear);
})->orWhereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('invoices as ri')
->whereColumn('ri.parent_id', 'u.id')
->where('ri.school_year', $schoolYear);
})->orWhereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('parent_accounts as pa')
->whereColumn('pa.parent_id', 'u.id')
->where('pa.school_year', $schoolYear);
})->orWhereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('emergency_contacts as ec')
->whereColumn('ec.parent_id', 'u.id')
->where('ec.school_year', $schoolYear);
});
};
}
private function familiesForParent(int $parentId, string $schoolYear): array
{
return DB::table('family_guardians as fg')
->join('families as f', 'f.id', '=', 'fg.family_id')
->where('fg.user_id', $parentId)
->whereExists(function ($exists) use ($schoolYear) {
$exists->selectRaw('1')
->from('family_students as fs')
->join('students as s', 's.id', '=', 'fs.student_id')
->whereColumn('fs.family_id', 'f.id')
->where('s.school_year', $schoolYear);
})
->orderBy('f.household_name')
->select('f.*', 'fg.relation', 'fg.is_primary', 'fg.receive_emails', 'fg.receive_sms')
->get()
->map(fn ($row) => (array) $row)
->all();
}
private function studentsForParent(int $parentId, string $schoolYear): array
{
$rows = DB::table('students as s')
->select('s.id', 's.firstname', 's.lastname', 's.parent_id')
->where('s.school_year', $schoolYear)
->where(function ($query) use ($parentId) {
$query->where('s.parent_id', $parentId)
->orWhereExists(function ($exists) use ($parentId) {
$exists->selectRaw('1')
->from('family_guardians as fg')
->join('family_students as fs', 'fs.family_id', '=', 'fg.family_id')
->whereColumn('fs.student_id', 's.id')
->where('fg.user_id', $parentId);
});
})
->orderBy('s.lastname')
->orderBy('s.firstname')
->get()
->map(fn ($row) => (array) $row)
->all();
foreach ($rows as &$row) {
$studentId = (int) ($row['id'] ?? 0);
$classSectionName = $studentId > 0
? (string) (StudentClass::getClassSectionsByStudentId($studentId, $schoolYear) ?? '')
: '';
$row['class_section_name'] = $classSectionName;
$row['grade'] = $classSectionName;
}
unset($row);
return $rows;
}
private function emergencyContactsForParents(array $guardians, string $schoolYear): array
{
$parentIds = array_values(array_filter(array_map(
static fn ($g) => (int) ($g['user_id'] ?? 0),
$guardians
)));
if ($parentIds === []) {
return [];
}
return 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)
->where('school_year', $schoolYear)
->orderByDesc('updated_at')
->get()
->map(fn ($row) => (array) $row)
->all();
}
}