Kembali ke Artikel
9 November 2025
vandyahmad24
Diperbarui: 18 April 2026

Tutorial Laravel 12 Job Batching: Implementasi, Progress, dan Error Handling

Bayangkan Anda perlu kirim email ke 5.000 pengguna sekaligus, atau proses 1.000 gambar setelah upload. Kalau dijalankan satu per satu lewat queue biasa, Anda tidak tahu kapan semuanya selesai, dan tidak bisa jalankan aksi “setelah semua beres”.

Job Batching di Laravel menyelesaikan masalah ini. Artikel ini membahas cara kerjanya, implementasi lengkap dengan contoh kode, dan cara handle error dalam batch.

Apa Itu Job Batching?

Job Batching memungkinkan Anda mengelompokkan beberapa job ke dalam satu batch, lalu mendefinisikan callback yang dijalankan:

  • Saat semua job berhasil (then)
  • Saat ada job yang gagal (catch)
  • Saat semua job selesai (berhasil atau gagal) (finally)

Setup: Membuat Tabel Batch

php artisan make:batches-table
php artisan migrate

Ini membuat tabel job_batches yang menyimpan status setiap batch.

Membuat Job yang Batchable

Job harus implement ShouldQueue dan gunakan trait Batchable:

<?php

namespace AppJobs;

use AppModelsUser;
use AppMailPromotionMail;
use IlluminateBusBatchable;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateQueueInteractsWithQueue;
use IlluminateSupportFacadesMail;

class SendPromotionEmail implements ShouldQueue
{
    use Batchable, Dispatchable, InteractsWithQueue, Queueable;

    public function __construct(
        private User $user
    ) {}

    public function handle(): void
    {
        // Cek apakah batch sudah di-cancel
        if ($this->batch()->cancelled()) {
            return;
        }

        Mail::to($this->user)->send(new PromotionMail());
    }
}

Mengirim Batch

use AppJobsSendPromotionEmail;
use IlluminateSupportFacadesBus;

$users = User::where('subscribed', true)->get();

$batch = Bus::batch(
    $users->map(fn ($user) => new SendPromotionEmail($user))->toArray()
)->then(function (Batch $batch) {
    // Dijalankan saat semua job berhasil
    Log::info("Batch selesai: {$batch->totalJobs} email terkirim.");
})->catch(function (Batch $batch, Throwable $e) {
    // Dijalankan saat ada job yang gagal
    Log::error("Batch error: {$e->getMessage()}");
})->finally(function (Batch $batch) {
    // Selalu dijalankan saat batch selesai (berhasil atau tidak)
    Cache::forget('promotion-batch-running');
})->name('Kirim Email Promosi')
  ->allowFailures()  // batch tetap lanjut meski ada job gagal
  ->dispatch();

// Simpan ID batch untuk monitor progress
return $batch->id;

Monitor Progress Batch

Ambil info batch berdasarkan ID:

use IlluminateSupportFacadesBus;

$batch = Bus::findBatch($batchId);

// Info yang tersedia
$batch->id;
$batch->name;
$batch->totalJobs;
$batch->pendingJobs;
$batch->failedJobs;
$batch->processedJobs();  // totalJobs - pendingJobs
$batch->progress();       // 0-100 persen
$batch->finished();       // apakah sudah selesai
$batch->cancelled();      // apakah di-cancel

Contoh endpoint API untuk polling progress:

Route::get('/batches/{batchId}/progress', function (string $batchId) {
    $batch = Bus::findBatch($batchId);

    if (!$batch) {
        return response()->json(['error' => 'Batch tidak ditemukan'], 404);
    }

    return response()->json([
        'progress'     => $batch->progress(),
        'total'        => $batch->totalJobs,
        'processed'    => $batch->processedJobs(),
        'failed'       => $batch->failedJobs,
        'finished'     => $batch->finished(),
    ]);
})->middleware('auth');

Cancel Batch

$batch = Bus::findBatch($batchId);
$batch->cancel();

Job yang belum diproses akan skip sendiri karena pengecekan $this->batch()->cancelled() di awal method handle().

Batch dengan Job Berantai

Anda bisa chain batch: jalankan batch kedua setelah batch pertama selesai:

Bus::batch([
    new ProcessImages($product),
    new GenerateThumbnails($product),
])->then(function (Batch $batch) use ($product) {
    // Setelah gambar dan thumbnail selesai, generate sitemap
    GenerateSitemap::dispatch();
})->dispatch();

Praktis: Batch untuk Impor CSV

Skenario umum: impor data dari file CSV besar.

<?php

namespace AppJobs;

use IlluminateBusBatchable;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;

class ImportCsvRow implements ShouldQueue
{
    use Batchable, Queueable;

    public function __construct(private array $row) {}

    public function handle(): void
    {
        if ($this->batch()->cancelled()) return;

        Product::updateOrCreate(
            ['sku'  => $this->row['sku']],
            ['name' => $this->row['name'], 'price' => $this->row['price']]
        );
    }
}

// Di controller
$rows  = CsvParser::parse($file);
$jobs  = collect($rows)->map(fn ($row) => new ImportCsvRow($row));

$batch = Bus::batch($jobs->toArray())
            ->name('Import Produk')
            ->allowFailures()
            ->dispatch();

return redirect()->route('imports.progress', $batch->id);

Baca Juga

Butuh tim untuk implementasi background processing di aplikasi Laravel Anda? Lihat layanan pengembangan aplikasi kami.

Tag: #laravel #php #tutorial
BACA JUGA

Artikel Lainnya di Kategori Laravel

Laravel

9 November 2025

Tutorial PostgreSQL di Laravel: Setup, JSONB, dan Full-Text Search

Laravel secara default menggunakan MySQL. Tapi kalau proyek Anda butuh fitur seperti JSON columns yang lebih canggih, full-text search bawaan, atau JSONB, PostgreSQL adalah pilihan yang solid. Artikel ini membahas cara setup PostgreSQL di Laravel, termasuk konfigurasi, perbedaan dengan MySQL, dan fitur-fitur PostgreSQL yang bisa dimanfaatkan langsung dari Eloquent. Instalasi dan Konfigurasi Pastikan extension PHP […]

Baca Artikel
Laravel

10 November 2025

Contoh Penggunaan Concurrency di Laravel 12: Dashboard, API Paralel, dan Defer

Artikel sebelumnya membahas konsep Concurrency di Laravel 12. Artikel ini fokus pada implementasi: studi kasus nyata bagaimana Concurrency bisa mempercepat aplikasi secara signifikan. Studi Kasus 1: Dashboard dengan Banyak Data Source Dashboard admin yang butuh data dari beberapa tabel berbeda. Ini biasanya jadi bottleneck karena diquery satu per satu. Sebelum (sequential — sekitar 800ms): public […]

Baca Artikel
Laravel

9 November 2025

Apa Itu Laravel Volt dan Bagaimana Cara Kerjanya

Kalau Anda pernah pakai Livewire di Laravel, mungkin sudah tahu betapa nyamannya bikin komponen reaktif tanpa harus tulis JavaScript. Laravel Volt membawa pengalaman itu selangkah lebih jauh dengan sintaks single-file component yang lebih bersih. Artikel ini menjelaskan apa itu Laravel Volt, bagaimana cara kerjanya, dan di mana Volt cocok dipakai. Apa Itu Laravel Volt? Laravel […]

Baca Artikel

Ingin Membaca Artikel Lainnya?

Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.

Lihat Semua Artikel