resolveAttachmentPath($path); } protected function resolveAttachmentPath(string $path): ?string { // CI stored paths may look like: writable/uploads/... $relative = preg_replace('#^writable/uploads/#', '', $path); // Laravel equivalent target $absolute = storage_path('app/uploads/' . ltrim((string) $relative, '/')); return is_file($absolute) ? $absolute : null; } /** * Returns map: [report_id => [ ['id'=>.., 'name'=>..], ... ]] */ protected function loadAttachmentsForReports(array $reportIds): array { $reportIds = array_values(array_filter(array_map('intval', $reportIds))); if (empty($reportIds)) { return []; } $rows = ClassProgressAttachment::query() ->whereIn('report_id', $reportIds) ->orderBy('id', 'asc') ->get(); $map = []; foreach ($rows as $row) { $reportId = (int) ($row->report_id ?? 0); if ($reportId === 0) { continue; } $map[$reportId][] = [ 'id' => (int) $row->id, 'name' => $row->original_name ?: basename((string) $row->file_path), ]; } return $map; } protected function decodeFlags(?string $json): array { if (! $json) { return []; } $flags = json_decode($json, true); return is_array($flags) ? $flags : []; } }