> */ public function parentStudents(int $parentId): array { if ($parentId <= 0) { return []; } $rows = DB::table('enrollments as e') ->select( 'e.student_id', 'e.class_section_id', 'e.updated_at', 'e.created_at', 's.firstname', 's.lastname', 'cs.class_section_name', ) ->join('students as s', 's.id', '=', 'e.student_id') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'e.class_section_id') ->where('e.parent_id', $parentId) ->where('e.is_withdrawn', 0) ->orderByDesc('e.updated_at') ->orderByDesc('e.created_at') ->get(); $students = []; foreach ($rows as $row) { $arr = (array) $row; $studentId = (int) ($arr['student_id'] ?? 0); if ($studentId === 0 || isset($students[$studentId])) { continue; } $students[$studentId] = $arr; } return array_values($students); } /** * legacy `getParentSectionIds`. * * @return list */ public function parentSectionIds(int $parentId): array { if ($parentId <= 0) { return []; } $ids = DB::table('enrollments') ->where('parent_id', $parentId) ->where('is_withdrawn', 0) ->whereNotNull('class_section_id') ->distinct() ->pluck('class_section_id') ->map(fn ($id) => (int) $id) ->filter(fn ($id) => $id > 0) ->unique() ->values() ->all(); return array_values($ids); } public function parentCanAccessSection(int $parentUserId, ?int $classSectionId): bool { if (! $classSectionId) { return false; } return in_array((int) $classSectionId, $this->parentSectionIds($parentUserId), true); } /** * Progress reports visible to the parent (all teachers) for allowed sections. * * @param list $sectionIds */ public function reportsForSections(array $sectionIds, ?string $schoolYear = null, ?string $semester = null): Collection { $sectionIds = array_values(array_filter(array_map('intval', $sectionIds))); if ($sectionIds === []) { return collect(); } $attachmentMap = []; $query = ClassProgressReport::query() ->select('class_progress_reports.*') ->addSelect([ 'cs.class_section_name', 'u.firstname as teacher_firstname', 'u.lastname as teacher_lastname', ]) ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'class_progress_reports.class_section_id') ->leftJoin('users as u', 'u.id', '=', 'class_progress_reports.teacher_id') ->whereIn('class_progress_reports.class_section_id', $sectionIds); $schoolYear = trim((string) $schoolYear); if ($schoolYear !== '' && Schema::hasColumn('class_progress_reports', 'school_year')) { $schoolYearRange = $this->schoolYearDateRange($schoolYear); $query->where(function ($yearQuery) use ($schoolYear, $schoolYearRange) { $yearQuery->where('class_progress_reports.school_year', $schoolYear) ->orWhere(function ($legacyQuery) use ($schoolYear, $schoolYearRange) { $legacyQuery->where(function ($blankQuery) { $blankQuery->whereNull('class_progress_reports.school_year') ->orWhere('class_progress_reports.school_year', ''); }); if ($schoolYearRange !== null) { $legacyQuery->whereBetween('class_progress_reports.week_start', [ $schoolYearRange['start'], $schoolYearRange['end'], ]); } else { $legacyQuery->where('cs.school_year', $schoolYear); } }); }); } $semester = trim((string) $semester); if ($semester !== '' && Schema::hasColumn('class_progress_reports', 'semester')) { $query->where(function ($semesterQuery) use ($semester) { $semesterQuery->where('class_progress_reports.semester', $semester) ->orWhere(function ($legacyQuery) use ($semester) { $legacyQuery->where(function ($blankQuery) { $blankQuery->whereNull('class_progress_reports.semester') ->orWhere('class_progress_reports.semester', ''); })->where('cs.semester', $semester); }); }); } $rows = $query->orderByDesc('class_progress_reports.week_start')->get(); $attachmentMap = $this->classProgressQuery->attachmentMap($rows->pluck('id')->all()); return $rows ->each(function (ClassProgressReport $row) use ($attachmentMap) { $row->setAttribute('teacher_name', trim((string) ($row->teacher_firstname ?? '').' '.(string) ($row->teacher_lastname ?? ''))); $row->setAttribute('attachments', $attachmentMap[$row->id] ?? []); }); } /** * @return array{start:string,end:string}|null */ private function schoolYearDateRange(string $schoolYear): ?array { $schoolYear = trim($schoolYear); if ($schoolYear === '') { return null; } if (Schema::hasTable('school_years')) { try { $row = DB::table('school_years') ->where('name', $schoolYear) ->first(['start_date', 'end_date']); if ($row && $row->start_date && $row->end_date) { return [ 'start' => substr((string) $row->start_date, 0, 10), 'end' => substr((string) $row->end_date, 0, 10), ]; } } catch (\Throwable) { } } if (preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $matches) === 1) { return [ 'start' => $matches[1].'-08-01', 'end' => $matches[2].'-07-31', ]; } return null; } /** * legacy `groupReportsByWeek` — keys `"{week_start}:{class_section_id}"`. * * @param iterable $rows * @return array> */ public function groupReportsByWeek(iterable $rows): array { $statusOptions = (array) config('progress.status_options', []); $reportGroups = []; foreach ($rows as $row) { $statusLabel = $statusOptions[$row->status] ?? 'Unknown'; $row->setAttribute('status_label', $statusLabel); $weekStart = $row->week_start instanceof CarbonInterface ? $row->week_start->format('Y-m-d') : (string) $row->week_start; $sectionId = (int) $row->class_section_id; if ($weekStart === '' || $sectionId === 0) { continue; } $key = $weekStart.':'.$sectionId; if (! isset($reportGroups[$key])) { $reportId = (int) $row->id; $reportGroups[$key] = [ 'id' => $reportId, 'report_id' => $reportId, 'view_url' => url('/api/v1/class-progress/'.$reportId), 'parent_view_url' => url('/api/v1/parents/progress/'.$reportId), 'week_start' => $weekStart, 'week_end' => $row->week_end instanceof CarbonInterface ? $row->week_end->format('Y-m-d') : (string) ($row->week_end ?? ''), 'class_section_name' => $row->class_section_name ?? '', 'class_section_id' => $sectionId, 'reports' => [], 'weekly_reports' => [], 'reports_array' => [], 'report_list' => [], 'subjects' => [], ]; } $subject = (string) ($row->subject ?? ''); if ($subject === '') { continue; } $report = (new ClassProgressReportResource($row))->toArray(request()); $reportGroups[$key]['reports'][$subject] = $report; $reportGroups[$key]['weekly_reports'][] = $report; $reportGroups[$key]['reports_array'][] = $report; $reportGroups[$key]['report_list'][] = $report; $reportGroups[$key]['subjects'][] = $subject; } return $reportGroups; } /** * Weekly batch for a section/week (legacy parent `view`, all subjects). * * @return list */ public function weeklyReportsForSectionWeek(int $classSectionId, string $weekStartYmd): array { $rows = ClassProgressReport::query() ->with(['classSection', 'teacher']) ->where('class_section_id', $classSectionId) ->whereDate('week_start', $weekStartYmd) ->orderBy('subject') ->get() ->all(); $attachments = $this->attachmentMapForReportIds(array_map(fn (ClassProgressReport $row) => (int) $row->id, $rows)); foreach ($rows as $row) { $row->setAttribute('attachments', $attachments[$row->id] ?? []); } return $rows; } /** * @return array> */ public function attachmentMapForReportIds(array $reportIds): array { return $this->classProgressQuery->attachmentMap($reportIds); } }