Daravelv1.25.0
ENID GitHub

Excel dan CSV

daravel_excel mengekspor dan mengimpor spreadsheet dengan concern dari Laravel Excel 3.1 (FromQuery, WithHeadings, ToModel, WithValidation, ...). Sheet ditulis sambil dibaca: baris diambil dari database per chunk lalu langsung dikirim sebagai XLSX terkompresi (atau CSV) ke client HTTP atau ke disk, sehingga ekspor 500.000 baris memakai memori yang sama dengan ekspor 10 baris.

dartisan pub:add excel

pub:add menambahkan dependency dan command-nya; dengan dart pub add daravel_excel, command ditambahkan sendiri. Tidak perlu setup lain: Excel memakai database, storage, dan validasi aplikasi.

Ekspor#

import 'package:daravel_excel/daravel_excel.dart';

final class StudentsExport
    implements FromQuery, WithHeadings, WithMapping, WithColumnFormatting {
  StudentsExport(this.schoolId);
  final int schoolId;

  @override
  Object query() => Student.query()
      .where('school_id', schoolId)
      .with_(['classroom'])
      .orderBy('name');

  @override
  List<Object?> headings() => ['NISN', 'Name', 'Class', 'Average', 'Registered'];

  @override
  List<Object?> map(Object? row) {
    final student = row! as Student;
    return [
      student.nisn,
      student.name.toUpperCase(),
      student.classroom?.name ?? '-',
      student.averageScore,
      student.createdAt, // DateTime menjadi tanggal Excel
    ];
  }

  @override
  Map<String, String> columnFormats() => {'D': NumberFormat.number00};
}
// Download yang di-stream (tipe file mengikuti ekstensi)
router.get('/reports/students.xlsx', (Request request) =>
    Excel.download(StudentsExport(1), 'students-2026.xlsx'));

// File di disk, lokal atau S3 (daravel_s3), juga di-stream
await Excel.store(StudentsExport(1), 'reports/students.xlsx', disk: 's3');

// Bytes atau stream
final bytes = await Excel.raw(StudentsExport(1), ExcelType.csv);
Concern
FromQuery ModelQuery (model, beserta eager load per chunk) atau QueryBuilder (map), dibaca 1.000 baris sekali jalan (WithCustomChunkSize)
FromCollection list atau iterable apa pun
FromGenerator sebuah Stream, dibaca sambil file ditulis
WithHeadings baris pertama (list of list untuk beberapa baris heading)
WithMapping mengubah baris sumber menjadi sel (list of list untuk beberapa baris)
WithTitle nama sheet (dibuat valid: maksimal 31 karakter, tanpa []:*?/\)
WithMultipleSheets satu sheet per export di sheets()
WithColumnFormatting format angka per kolom, 'D' atau 'B:D' (NumberFormat)
WithColumnWidths lebar per kolom
WithCustomCsvSettings delimiter, enclosure, akhir baris, byte order mark

Tanpa WithMapping, model ditulis sebagai nilai JSON-nya, map sebagai nilainya, dan list apa adanya. Di XLSX, angka, boolean, dan tanggal tetap bertipe (DateTime tepat tengah malam diformat sebagai tanggal, selain itu sebagai tanggal dan jam); teks ditulis inline, sehingga tidak ada tabel shared strings yang membesar di memori. CSV membungkus setiap field dengan tanda kutip, seperti writer PhpSpreadsheet, dan hanya berisi sheet pertama.

Berbeda dengan Laravel Excel, tidak ada ShouldAutoSize: nilai terlebar sebuah kolom baru diketahui setelah semua baris ditulis, padahal file di-stream. Gunakan WithColumnWidths.

Impor#

final class StudentsImport
    with WithHeadingRow, SkipsEmptyRows
    implements ToModel, WithValidation, WithBatchInserts {
  StudentsImport(this.schoolId);
  final int schoolId;

  @override
  Model? model(Map<String, Object?> row) => Student.definition.newInstance({
    'school_id': schoolId,
    'nisn': '${row['nisn']}',
    'name': row['full_name'],
    'average_score': row['average'],
  });

  @override
  Map<String, Object> rules() => {
    'nisn': 'required|unique:students,nisn',
    'full_name': 'required|string|max:150',
    'average': 'nullable|numeric|between:0,100',
  };

  @override
  int get batchSize => 500;
}
router.post('/students/import', (Request request) async {
  await Excel.import(StudentsImport(1), (await request.file('spreadsheet'))!);
  return {'message': 'Imported.'};
});

WithHeadingRow memberi key pada setiap baris dari sel baris pertama, di-slug dengan _ (Full Name → full_name); heading kosong memakai indeks kolomnya sebagai key. Tanpa concern ini, key baris adalah '0', '1', .... Sumbernya bisa UploadedFile, path di disk (Excel.import(import, 'in/file.xlsx', disk: 's3')), atau bytes.

Impor berjalan dalam satu transaksi database. Bila sebuah baris gagal validasi, impor melempar ExcelValidationException, yaitu ValidationException yang error-nya ber-key <baris>.<atribut> dengan nomor baris di sheet, sehingga exception handler menjawab 422 bergaya Laravel dan tidak ada data yang tersimpan:

{
  "message": "The email field must be a valid email address. (and 1 more error)",
  "errors": {
    "3.email": ["The email field must be a valid email address."],
    "3.average": ["The average field must be between 0 and 100."]
  }
}

e.failures berisi daftar Failure(row, attribute, errors, values), seperti failures() di Laravel Excel. Dengan SkipsOnFailure (atau mixin SkipsFailures, yang menyimpannya di failures()), baris yang gagal dilewati dan baris lainnya tetap disimpan.

Concern
ToModel baris menjadi model; model yang belum tersimpan akan disimpan, null melewati baris
ToCollection semua baris sekaligus, atau per chunk dengan WithChunkReading
OnEachRow setiap baris beserta nomor barisnya di sheet (ExcelRow.index)
WithHeadingRow baris ber-key heading (headingRow() default 1)
WithStartRow / WithLimit baris awal, jumlah baris
WithValidation rule Daravel per baris ('*.email' juga diterima)
WithCustomValidationMessages / WithCustomValidationAttributes pesan dan nama atribut
WithChunkReading baris diproses per chunkSize
WithBatchInserts satu INSERT per batchSize model (tanpa event model)
SkipsEmptyRows lewati baris yang semua selnya kosong
SkipsOnFailure / SkipsFailures lewati baris gagal alih-alih berhenti
WithSheetSelection / WithMultipleSheetImports pilih sheet berdasarkan indeks atau nama, atau satu import per sheet

Sel terbaca sebagai int, double, bool, atau String. Sel tanggal berupa nomor seri Excel, seperti yang dikembalikan Laravel Excel: ubah dengan ExcelDate.toDateTime(row['date']! as num). Sel formula memberi nilai yang dihitung Excel saat file disimpan. Di CSV, true dan false (huruf besar atau kecil) menjadi boolean dan teks berbentuk angka menjadi angka kecuali diawali nol (0123 tetap teks), seperti cara PhpSpreadsheet membacanya, dan delimiter dideteksi di antara ,, ;, tab, dan |.

Excel.toArray(source, headingRow: true) mengembalikan baris dari semua sheet.

Command#

// bootstrap/commands.dart
import 'package:daravel_excel/commands.dart';

final List<CommandFactory> commands = [...excelCommands];
dartisan make:export StudentsExport --model=Student   # lib/app/exports/
dartisan make:import StudentsImport --model=Student   # lib/app/imports/

Queue#

Export adalah class di aplikasi Anda, jadi ekspor lewat queue adalah job Anda sendiri yang memanggil Excel.store (didaftarkan di bootstrap/jobs.dart seperti job lainnya):

final class ExportStudents extends Job {
  const ExportStudents(this.schoolId);
  final int schoolId;

  factory ExportStudents.fromJson(Map<String, Object?> json) =>
      ExportStudents(json['school_id']! as int);

  @override
  String get jobName => 'export-students';

  @override
  Map<String, Object?> toJson() => {'school_id': schoolId};

  @override
  Duration get timeout => const Duration(minutes: 30);

  @override
  Future<void> handle(JobContext ctx) =>
      Excel.store(StudentsExport(schoolId), 'reports/$schoolId.xlsx', disk: 's3');
}

Memori#

bench/bin/excel_memory.dart menulis sepuluh kolom bertipe campuran (build AOT, Windows 11, Dart 3.13.4):

Baris Tipe File Waktu Puncak memori
10.000 XLSX 0,5 MB 0,2 s 24 MB
100.000 XLSX 5,0 MB 1,9 s 24 MB
500.000 XLSX 25,1 MB 9,9 s 24 MB
500.000 CSV 82,4 MB 2,4 s 24 MB

Angka memori adalah puncak seluruh proses, termasuk runtime; nilainya tidak bertambah seiring jumlah baris. Pembacaan juga men-stream sheet dengan cara yang sama; shared strings sebuah XLSX (teks yang disimpan Excel sekali per workbook) disimpan di memori selama workbook dibaca.

File Laravel Excel#

bench/interop/excel.sh menjalankan Laravel 12 dengan maatwebsite/excel 3.1 dan Daravel: masing-masing membaca file XLSX dan CSV yang ditulis pihak lain, lengkap dengan teks, angka, boolean, sel kosong, dan tanggal.

Padanan Laravel#

Bagian lain dari Laravel yang ikut didukung, beserta API yang bisa dicari di pub.dev.

Laravel Daravel API Catatan
php artisan make:export UsersExport --model=User dartisan make:export UsersExport --model=User MakeExportCommand