Export Million Data Ke CSV Di Laravel - CRUDPRO

Export Million Data Ke CSV Di Laravel

Export Million Data Ke CSV Di Laravel

Pada versi 6.0, Laravel menambahkan sebuah feature baru yakni Lazy Collection. Dengan Lazy Collection, aplikasi Laravel bisa mengambil beberapa kumpulan data dari database tanpa harus memuat semua data itu di dalam memory. Dibalik layar, Lazy Collection manfaatkan feature PHP Generator. Beberapa lalu, saya posting video untuk menerangkan bagaimana generator bisa mengirit penggunaan memory, video memiliki durasi 20 menitan itu bisa membantu kamu agar semakin memahami apa yang saya tuliskan di sini.

Pada artikel ini, saya ingin sharing bagaimanakah cara memanfaatkan Lazy Collection untuk melakukan streaming file CSV, hingga kita bisa melakukan ekspor jutaan data tanpa harus memuat jutaan data itu ke memory.

Kenapa CSV?

Kenapa menggunakan CSV?, kenapa bukan file excel saja?

Setahu saya, revisi jika salah ya. Saat export data jadi file excel, yang terjadi ialah (jutaan) data kamu akan dibuffer/dimuat di dalam content file excel, sesudah usai, file tersebut diproses kembali untuk dilakukan proses encoding, styling (membuat border, beri warna text, merge-cells, dsb), hingga kemudian buntelan file excel kamu siap, baru file excel kamu "disajikan" ke browser.

Saat "memasak" (baca: mengolah) file excel itu, makin bertambah "porsi" (baca: baris) yang ingin dimasak, makin besar juga "wajan" (baca: memory) yang perlu dipakai untuk "memasak" file itu. Jika "wajan"-nya kurang cukup besar, aplikasi kamu akan error, karena memorinya tidak mencukupi.

Tidak sama dengan CSV, file CSV cuma berisi text biasa (plain teks) yang tiap barisnya mewakili baris pada tabel, di mana pada tiap baris ada watak ";" atau "," sebagai pembatas dari tiap-tiap kolom. Kekurangannya, pada file CSV kita tidak dapat melakukan styling, formula, dll sama seperti yang didukung oleh pola excel. Tetapi dengan kekurangan itu, kita bisa lakukan streaming content CSV baris-per-baris, dari server dialirkan ke browser, dari browser dialirkan ke storage device pemakai. Singkatnya: server kita tak perlu memuat beban memory dari semua data dalam file CSV yang akan di-export.

Kenapa Lazy Collection?

Jika kamu pernah melihat video yang saya jelaskan di awal, kamu akan mengerti peran Lazy Collection di sini. Singkatnya, dengan Lazy Collection kita tak perlu memuat data yang ingin kita export, hingga yang terjadi ialah data hasil dari query akan difetch satu-per-satu dari DBMS, untuk selanjutnya data itu ditulis di dalam stream file CSV yang akan diterima dan diolah langsung oleh web browser.

Secara keseluruhan, deskripsi prosesnya kelak bakal menjadi (kurang-lebih) seperti ini:

Export Million Data Ke CSV Di Laravel

Bagaimana Caranya?

Caranya sebenarnya simpel. Misalnya di sini kita mempunyai mode AppModelsLogActivity.php yang mewakili table log_activities yang berisi 10 juta data. Dalam table itu, kita ingin ekspor data time, user_id, message, ip_address, dan user_agent.

Supaya cukup panjang artikelnya, misalnya isi file modelnya seperti ini:

<?php

namespace Illuminate\Database\Eloquent\Model;

class LogActivity extends Model
{

    protected $table = "log_activities";

}

Kemudian, kita menambahkan route seperti ini di routes/web.php:

Route::get('log-activities/export', 'LogActivityController@export');

Kemudian, pada controllernya kamu bisa menuliskan seperti ini:

<?php

namespace App\Http\Controllers;

use App\Models\LogActivity;
use Illuminate\Http\Request;

class LogActivityController extends Controller
{

    public function export()
    {
        // 1. Ambil seluruh data log_activities kedalam LazyCollection (Generator)
        $logs = LogActivity::orderBy('time')->cursor();

        // FYI: dibawah ini contoh kalau kamu mau gunakan condition dengan cursor
        // $logs = LogActivity::whereRaw("DATE(time) = '2019-11-08'")->where('user_id', 1)->cursor()

        // 2. Set header untuk streaming file CSV
        $filename = "log-activities.csv";
        header("Content-type: text/csv");
        header("Content-Disposition: attachment; filename={$filename}");

        // 3. Stream file CSV
        $csv = fopen("php://output", "w+");
        // 3.a. Tulis table header
        fputcsv($csv, ["Time", "User ID", "Message", "IP Address", "User Agent"]);
        // 3.b. Tulis baris setiap log
        foreach ($logs as $log) {
            fputcsv($csv, [
                $log->time,
                $log->user_id,
                $log->message,
                $log->ip_address,
                $log->user_agent
            ]);
        }
        // 3.c. Tutup file
        fclose($csv);
    }

}

Selesai. Dengan demikian, saat kamu mengakses https://appkamu.com/log-activities/ekspor, aplikasi kamu akan mengirimkan response berbentuk streaming file CSV yang berisi semua data di dalam log_activities tanpa harus mengalami beban memory yang tinggi.

Ngomong-ngomong, cara di atas ialah cara yang agak native karena kita langsung memakai fungsi header bawaan PHP. Saya catat seperti itu agar kamu tahu cara di native atau di frame-work lain seperti apa. Jika kamu ingin tahu cara yang "lebih" ke-Laravel-an, kamu bisa memakai fungsi streamDownload seperti di bawah ini:

<?php

namespace App\Http\Controllers;

use App\Models\LogActivity;
use Illuminate\Http\Request;

class LogActivityController extends Controller
{

    public function export()
    {
        $logs = LogActivity::orderBy('time')->cursor();
        $filename = "log-activities.csv";

        return response()->streamDownload(function() use ($logs) {
            $csv = fopen("php://output", "w+");

            fputcsv($csv, ["Time", "User ID", "Message", "IP Address", "User Agent"]);

            foreach ($logs as $log) {
                fputcsv($csv, [
                    $log->time,
                    $log->user_id,
                    $log->message,
                    $log->ip_address,
                    $log->user_agent
                ]);
            }

            fclose($csv);
        }, $filename, ["Content-type" => "text/csv"]);
    }

}

Begitulah cara melakukan export CSV dengan aman di Laravel. Jika ada saran, masukan, atau pertanyaan, silahkan tinggalkan pertanyaan anda di kolom komentar ya. Semoga bermanfaat.