Laravel Inertia Vue 3: Real-Time Event & Export Excel
Lanjutan Laravel Inertia Vue 3 Bagian 3 berfokus pada orkestrasi pemrosesan latar belakang (background queue), penyiaran peristiwa waktu nyata (real-time broadcasting) menggunakan Laravel Reverb, serta ekspor laporan Excel berskala besar tanpa memblokir antarmuka pengguna. Mengintegrasikan ketiga komponen ini mengubah aplikasi SPA Anda menjadi sistem Enterprise Architecture dan platform SaaS responsif yang sanggup melayani ratusan pengguna bersamaan tanpa memicu galat Gateway Timeout.
Pada bagian kedua sebelumnya, kita telah mengamankan jalur mutasi data melalui RBAC, paginasi sisi server, dan unggah berkas FormData. Namun, saat saya mengawal peluncuran modul pengadaan barang milik klien ritel pada infrastruktur Cloud VPS bulan lalu, sistem kembali menghadapi kebuntuan baru ketika staf keuangan mencoba mengekspor 120.000 baris transaksi secara sinkron dan dua admin menyunting stok barang di detik yang sama.
Tanpa mekanisme antrean asinkron dan pembaruan status via WebSocket, proses ekspor langsung menghabiskan alokasi memori pekerja PHP-FPM sementara admin lain menimpa perubahan data yang sudah usang. Dalam panduan lanjutan ini, saya akan membedah cara merancang jalur ekspor berbasis Redis Queue, menyiarkan progres dan pembaruan tabel lewat Laravel Reverb, serta mengelola siklus hidup kanal privat di Vue 3.
1. Prasyarat Teknis & Komparasi Arsitektur Pemrosesan Data
Untuk menjalankan seluruh kode dalam panduan ini, pastikan lingkungan pengembangan dan server produksi Anda sudah dilengkapi komponen berikut: PHP 8.3+, Laravel 11 atau 12, Laravel Reverb (server WebSocket resmi bawaan Laravel), Laravel Echo & Pusher JS di sisi klien, serta Redis Server sebagai penggerak antrean (queue driver).
Banyak pengembang pemula masih memaksakan tugas berat seperti pembuatan laporan spreadsheet atau pembaruan dasbor langsung di dalam siklus permintaan HTTP standar. Tabel berikut membandingkan dampak performa antara pemrosesan sinkron konvensional versus arsitektur Event-Driven pada Laravel Inertia Vue 3:
Skenario Beban Kerja | Pendekatan Sinkron (HTTP Blocking) | Arsitektur Queue + Laravel Reverb | Dampak Infrastruktur Cloud |
|---|---|---|---|
Export 100.000+ Baris | Memicu | Diproses bertahap via | Konsumsi memori stabil di bawah 64 MB per worker. |
Sinkronisasi Tabel Multi-User | Harus menekan tombol refresh manual atau polling tiap 3 detik. | Server mendorong sinyal event melalui kanal WebSocket privat. | Menghemat hingga 85% kueri baca berulang ke basis data. |
Pengalaman Pengguna (UX) | Layar peramban membeku selama proses unduhan berlangsung. | Pengguna tetap bebas bernavigasi hingga tautan unduhan muncul. | Meningkatkan retensi pengguna pada aplikasi SaaS B2B. |
Keamanan Kanal | Rentan kebocoran endpoint publik jika tanpa validasi token. | Otorisasi otomatis melalui | Menjamin standar API Security lintas penyewa (tenant). |
2. Langkah 1: Konfigurasi Laravel Reverb & Otorisasi Kanal Privat
Setelah memasang dukungan penyiaran melalui perintah php artisan install:broadcasting, langkah terpenting adalah mengunci saluran komunikasi agar hanya pengguna terautentikasi yang berhak menerima pemberitahuan berkas laporan maupun pembaruan stok gudang. Buka berkas routes/channels.php dan daftarkan aturan otorisasi kanal privat pengguna.
<?php
use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
// Kanal privat khusus notifikasi dan tautan unduhan milik masing-masing pengguna
Broadcast::channel('users.{id}', function (User $user, int $id) {
return (int) $user->id === (int) $id;
});
// Kanal bersama bagi seluruh staf terautentikasi untuk sinkronisasi tabel produk
Broadcast::channel('inventory.updates', function (User $user) {
return in_array($user->role, ['admin', 'warehouse_staff']);
});Bedah Penjelasan Kode Channels: Metode Broadcast::channel() bertindak sebagai penjaga gerbang API Security sebelum koneksi WebSocket diizinkan berlangganan pada kanal tertentu. Dengan memanfaatkan sesi cookie Laravel yang sama dengan Inertia, server memverifikasi kecocokan $user->id dan peran pengguna tanpa perlu mengelola token rahasia secara manual di sisi klien.
Tips Praktis & Optimasi Performa dari Pengalaman Saya: Saat menjalankan Laravel Reverb di balik reverse proxy Nginx pada server produksi, pastikan Anda mengatur direktif
proxy_read_timeout 3600s;pada blok lokasi/appdan menaikkan batasulimit -n 65535pada sistem operasi Linux. Dalam proyek logistik yang menangani 1.500 koneksi konkuren, konfigurasi bawaan Linux sebesar 1.024 open files langsung menolak koneksi WebSocket baru begitu jam sibuk operasional dimulai.
3. Langkah 2: Membangun Background Job Export CSV/Excel & Event Broadcast
Alih-alih membebani peramban menunggu pembuatan berkas laporan, kita membungkus proses penulisan CSV menggunakan metode chunk() di dalam kelas ExportProductsJob. Begitu berkas selesai disimpan ke Cloud Storage atau disk publik, pekerjaan ini langsung memancarkan event ExportCompleted ke kanal privat pengguna peminta.
<?php
namespace App\Jobs;
use App\Events\ExportCompleted;
use App\Models\Product;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Storage;
class ExportProductsJob implements ShouldQueue
{
use Queueable;
public int $timeout = 300;
public int $tries = 3;
public function __construct(
public int $userId,
public ?string $searchFilter = null
) {}
public function handle(): void
{
$fileName = "exports/laporan-produk-{$this->userId}-" . now()->timestamp . '.csv';
$stream = fopen('php://temp', 'r+');
// Header kolom CSV
fputcsv($stream, ['ID', 'SKU', 'Nama Produk', 'Stok', 'Harga']);
Product::query()
->when($this->searchFilter, fn ($q, $s) => $q->where('name', 'like', "%{$s}%"))
->orderBy('id')
->chunk(1000, function ($products) use ($stream) {
foreach ($products as $product) {
fputcsv($stream, [
$product->id,
$product->sku,
$product->name,
$product->stock,
$product->price,
]);
}
});
rewind($stream);
Storage::disk('public')->put($fileName, stream_get_contents($stream));
fclose($stream);
// Siarkan URL unduhan langsung ke komponen Vue 3 pengguna terkait
broadcast(new ExportCompleted(
userId: $this->userId,
downloadUrl: Storage::disk('public')->url($fileName),
fileName: basename($fileName)
));
}
}Bedah Penjelasan Kode Job & Stream: Penggunaan aliran memori sementara php://temp yang dipadukan dengan chunk(1000) mencegah PHP memuat seluruh model Eloquent ke dalam RAM secara bersamaan. Properti $tries = 3 memastikan antrean mencoba memproses ulang secara otomatis apabila terjadi gangguan koneksi sementara pada basis data maupun penyimpanan objek.

4. Langkah 3: Mendengarkan Event di Vue 3 & Partial Reload Tanpa Kedip
Di sisi antarmuka Vue 3, kita memanfaatkan lifecycle hooks onMounted dan onUnmounted untuk mendaftarkan dan membersihkan pendengar Laravel Echo. Ketika sinyal ProductStockUpdated diterima, Inertia melakukan partial reload hanya pada prop products tanpa mengganggu input formulir yang sedang diketik pengguna.
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { router, usePage } from '@inertiajs/vue3';
const props = defineProps({
products: Object,
filters: Object,
});
const page = usePage();
const userId = page.props.auth.user.id;
const isExporting = ref(false);
const readyDownload = ref(null);
// Memicu background job ekspor dari tombol antarmuka
const triggerExport = () => {
isExporting.value = true;
readyDownload.value = null;
router.post(
'/products/export',
{ search: props.filters.search },
{
preserveScroll: true,
preserveState: true,
}
);
};
onMounted(() => {
// 1. Dengarkan notifikasi ekspor selesai khusus untuk pengguna ini
window.Echo.private(`users.${userId}`)
.listen('ExportCompleted', (event) => {
isExporting.value = false;
readyDownload.value = {
url: event.downloadUrl,
name: event.fileName,
};
});
// 2. Dengarkan perubahan stok real-time dari rekan tim lain
window.Echo.private('inventory.updates')
.listen('ProductStockUpdated', () => {
router.reload({
only: ['products'],
preserveScroll: true,
preserveState: true,
});
});
});
// WAJIB: Lepaskan kanal saat komponen dihancurkan untuk mencegah memory leak
onUnmounted(() => {
window.Echo.leave(`users.${userId}`);
window.Echo.leave('inventory.updates');
});
</script>
<template>
<div class="p-6 max-w-5xl mx-auto">
<div class="flex items-center gap-3 mb-6">
<button
@click="triggerExport"
:disabled="isExporting"
class="bg-emerald-600 text-white px-4 py-2 rounded-lg font-medium disabled:opacity-50"
>
{{ isExporting ? 'Memproses Laporan di Latar Belakang...' : 'Export CSV' }}
</button>
<a
v-if="readyDownload"
:href="readyDownload.url"
download
class="bg-blue-50 text-blue-700 border border-blue-200 px-4 py-2 rounded-lg text-sm font-semibold"
>
Unduh Berkas Siap: {{ readyDownload.name }}
</a>
</div>
</div>
</template>Bedah Penjelasan Kode Vue 3: Pemanggilan router.reload({ only: ['products'] }) di dalam pendengar ProductStockUpdated menginstruksikan Inertia mengambil potongan data tabel terbaru dari server secara diam-diam. Sementara itu, blok onUnmounted() memanggil window.Echo.leave() guna memastikan peramban tidak menumpuk pendengar duplikat setiap kali pengguna berpindah halaman SPA.

Troubleshooting & Skenario Error Handling di Lapangan: Masalah paling klasik saat menerapkan penyiaran event di lingkungan produksi adalah race condition di mana pekerja antrean memancarkan
ExportCompletedatau pembaruan stok sebelum transaksi basis data selesai di-commit, atau munculnya galatBroadcastException: Pusher error: cURL error 7: Failed to connect to localhost port 8080akibat variabelREVERB_HOSTdanVITE_REVERB_HOSTtidak sinkron antara backend dan hasil kompilasi Vite. Selalu implementasikan antarmukaShouldBroadcastAfterCommitpada kelas Event Anda dan jalankannpm run buildulang setiap kali mengubah konfigurasi host WebSocket di berkas.env.
5. FAQ Teknis: Pertanyaan Seputar Laravel Reverb, Queue & Inertia
1. Bagaimana cara menjaga proses php artisan reverb:start dan queue:work tetap hidup di server Linux?
Gunakan manajer proses seperti Supervisor atau layanan systemd di server Ubuntu/Debian Anda. Supervisor akan secara otomatis menyalakan ulang pekerja antrean maupun server WebSocket Reverb dalam hitungan detik apabila terjadi penghentian proses atau setelah Anda melakukan deployment kode baru.
2. Mengapa event dari Laravel Echo tereksekusi dua atau tiga kali setelah berpindah-pindah halaman SPA?
Hal ini terjadi karena Anda lupa memanggil window.Echo.leave() atau stopListening() di dalam hook onUnmounted() komponen Vue 3. Karena arsitektur SPA Inertia tidak memuat ulang halaman penuh, listener lama tetap aktif di memori peramban jika tidak dibersihkan saat komponen ditutup.
3. Bagaimana cara membersihkan berkas hasil export di folder storage agar tidak memenuhi harddisk server?
Jadwalkan perintah pembersihan otomatis di dalam berkas routes/console.php menggunakan Schedule::call() yang berjalan setiap malam untuk menghapus seluruh berkas di direktori storage/app/public/exports yang usianya lebih dari 24 jam.
4. Apakah Laravel Reverb membutuhkan biaya langganan pihak ketiga seperti Pusher atau Ably?
Tidak. Laravel Reverb adalah paket resmi sumber terbuka yang berjalan langsung di atas infrastruktur server Anda sendiri tanpa batasan kuota pesan harian maupun biaya langganan per koneksi konkuren.
Diskusi & Komentar
0Sampaikan pertanyaan teknis, masukan, atau feedback Anda seputar artikel ini.
Belum Ada Komentar
Jadilah pembaca pertama yang memulai diskusi menarik atau bertanya seputar materi artikel ini!