underscore. * * @return array> */ public static function read(string $path, ?string $extension = null): array { $extension = strtolower($extension ?? pathinfo($path, PATHINFO_EXTENSION)); $reader = $extension === 'csv' ? new CsvReader() : new XlsxReader(); $reader->open($path); $headers = []; $rows = []; foreach ($reader->getSheetIterator() as $sheet) { foreach ($sheet->getRowIterator() as $index => $row) { $cells = array_map( static fn ($v) => is_string($v) ? trim($v) : (is_null($v) ? '' : (string) $v), $row->toArray() ); if ($index === 1) { $headers = array_map(self::normalizeHeader(...), $cells); continue; } // Langkau baris kosong sepenuhnya if (count(array_filter($cells, static fn ($v) => $v !== '')) === 0) { continue; } $assoc = []; foreach ($headers as $i => $key) { if ($key === '') { continue; } $assoc[$key] = $cells[$i] ?? ''; } $rows[] = $assoc; } break; // hanya sheet pertama } $reader->close(); return $rows; } private static function normalizeHeader(string $header): string { $header = strtolower(trim($header)); $header = preg_replace('/[\s\-]+/', '_', $header); return preg_replace('/[^a-z0-9_]/', '', $header); } /** * Hantar muat turun XLSX (atau CSV) terus ke pelayar. * * @param array $headings * @param iterable> $rows */ public static function download(string $filename, array $headings, iterable $rows, string $format = 'xlsx'): StreamedResponse { return response()->streamDownload(function () use ($headings, $rows, $format) { $writer = $format === 'csv' ? new CsvWriter() : new XlsxWriter(); $writer->openToFile('php://output'); $writer->addRow(SpoutRow::fromValues($headings)); foreach ($rows as $row) { $writer->addRow(SpoutRow::fromValues($row)); } $writer->close(); }, $filename, [ 'Content-Type' => $format === 'csv' ? 'text/csv' : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ]); } }