first
This commit is contained in:
93
app/Support/Spreadsheet.php
Normal file
93
app/Support/Spreadsheet.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use OpenSpout\Common\Entity\Row as SpoutRow;
|
||||
use OpenSpout\Reader\CSV\Reader as CsvReader;
|
||||
use OpenSpout\Reader\XLSX\Reader as XlsxReader;
|
||||
use OpenSpout\Writer\CSV\Writer as CsvWriter;
|
||||
use OpenSpout\Writer\XLSX\Writer as XlsxWriter;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class Spreadsheet
|
||||
{
|
||||
/**
|
||||
* Baca fail CSV/XLSX menjadi senarai baris bersekutu (keyed by header).
|
||||
* Header dinormalkan: huruf kecil, ruang/sempang -> underscore.
|
||||
*
|
||||
* @return array<int, array<string, string>>
|
||||
*/
|
||||
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<int, string> $headings
|
||||
* @param iterable<int, array<int, scalar|null>> $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',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user