Pernah buka Firebase Console, klik menu Cloud Messaging, lalu takjub betapa gampangnya mengatur jam tayang push notification ke jutaan user? Semuanya tampak instan. Tapi rasa senang itu biasanya runtuh seketika saat kamu mulai buka IDE, bikin custom backend, dan menembak FCM via Postman atau API langsung. Parameter scheduling yang kamu cari ternyata nihil. Google sama sekali tidak menyediakan payload penjadwalan di level REST API-nya.
Kamu dipaksa memutar otak: apakah backend harus standby 24 jam penuh hanya untuk menahan request, atau ada cara yang jauh lebih elegan tanpa bikin server ngelag?
Jawabannya adalah mendistribusikan beban kerja tersebut ke sistem antrean. Mengakali limitasi API FCM bukan berarti harus membangun arsitektur rumit dari nol. Dengan ekosistem Laravel Queue, kamu bisa memarkir payload notifikasi dan mengeksekusinya tepat di waktu yang kamu mau, sembari menjaga response time server tetap instan.
Mengapa Harus Queue, Bukan Eksekusi Langsung?
Menembak HTTP request ke API pihak ketiga seperti Firebase membutuhkan waktu tunggu jaringan. Kalau controller kamu mengeksekusi cURL secara sinkron saat user menekan tombol di dashboard admin, web browser bakal tertahan (hanging) beberapa detik.
Laravel Queue memecah beban kerja ini menjadi background process. Begitu request masuk, server cuma butuh hitungan milidetik untuk memasukkan job ke antrean, lalu langsung melempar respons sukses ke pengguna. Laravel menyediakan beberapa pilihan backend antrean yang bisa dipilih sesuai kebutuhan:
| Queue Driver | Karakteristik Utama |
|---|---|
| database | Simpel, tanpa setup server tambahan, ideal untuk skala awal hingga menengah. |
| Redis | Super cepat, berjalan in-memory, sangat efisien menangani throughput masif. |
| Amazon SQS | Fully managed message queuing service dari AWS, highly scalable. |
| Beanstalkd | Khusus antrean kerja ringan dengan latensi rendah. |
Untuk implementasi kali ini, kita gunakan driver database yang paling minim friksi karena tidak menuntut kamu memasang service tambahan seperti Redis di server lokal.
Setup Awal: Siapkan Jalur Database
Langkah paling dasar dimulai dari file environment. Buka .env di root project kamu, lalu arahkan driver antrean ke database.
envAPP_NAME=Laravel APP_ENV=local APP_KEY=base64:3k123123lm= APP_DEBUG=true APP_URL=http://localhost:8000 LOG_CHANNEL=stack LOG_LEVEL=debug DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=db DB_USERNAME=root DB_PASSWORD= BROADCAST_DRIVER=log CACHE_DRIVER=file FILESYSTEM_DRIVER=local QUEUE_CONNECTION=database SESSION_DRIVER=file SESSION_LIFETIME=120
Pastikan variabel QUEUE_CONNECTION sudah bernilai database. Setelah itu, Laravel butuh wadah fisik berupa tabel database untuk menampung antrean job yang belum matang. Jalankan generator migrasi tabel antrean dan eksekusi skemanya:
bashphp artisan queue:table php artisan migrate
Database kamu sekarang punya tabel baru bernama jobs yang siap diisi daftar tugas tertunda.
Membangun Logika Notifikasi di Dalam Job Class
Sekarang kita racik payload FCM di dalam dedicated Job. Buat filenya melalui terminal:
bashphp artisan make:job NotificationJob
Buka file app/Jobs/NotificationJob.php yang baru dibuat, lalu masukkan struktur kodenya seperti ini:
php<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class NotificationJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $dongeng; public $tries = 3; /** * Create a new job instance. * * @return void */ public function __construct($dongeng) { $this->dongeng = $dongeng; } /** * Execute the job. * * @return void */ public function handle() { $url = 'https://fcm.googleapis.com/fcm/send'; /* API key diambil dari: Firebase Console -> Project Settings -> Cloud Messaging -> Server key */ $api_key = env('API_KEYFCM'); $fields = array( 'to' => "/topics/dongeng", 'collapse_key' => "type_a", 'notification' => array( "body" => "Body Desc", "title" => "Title Desc", "image" => "https://image.jpg" ), 'android' => array( "notification" => array( "image" => "https://image.jpg" ) ), 'data' => array( "body" => "Body Desc", "title" => "Title Desc", "link" => "https://image.jpg" ) ); $headers = array( 'Content-Type:application/json', 'Authorization:key=' . $api_key ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields)); $result = curl_exec($ch); if ($result === FALSE) { die('FCM Send Error: ' . curl_error($ch)); } curl_close($ch); return $result; } public function failed() { // Logika fallback ketika job gagal dieksekusi setelah batas maksimal percobaan ($tries) } }
Variabel $dongeng di constructor bekerja sebagai penampung data entity dari controller. Properti $tries = 3 memberi jaminan jika request cURL ke Firebase mendadak gagal akibat masalah jaringan sesaat, antrean akan mencoba menembaknya ulang otomatis hingga tiga kali sebelum menyerah dan masuk ke method failed().
Meracik Controller untuk Menentukan Jadwal Tembak
Tahap penentu ada di Controller. Di sinilah kita mengukur jarak waktu antara sekarang dengan jam target pengiriman notifikasi.
Generate controllernya terlebih dahulu:
bashphp artisan make:controller NotificationController
Buka app/Http/Controllers/NotificationController.php dan susun fungsinya:
phppublic function sendnotification(Request $request) { $dongeng = Post::find($request->id); // Hitung selisih waktu dari sekarang menuju jam 17:00:00 hari ini dalam satuan menit $delay = Carbon::parse()->floatDiffInMinutes('17:00:00'); // Masukkan job ke antrean database dengan jeda waktu delay dispatch(new NotificationJob($dongeng))->delay(now()->addMinutes($delay)); return redirect()->route('posts.index') ->with('success', 'Notification Dongeng ' . $dongeng->title . ' will send in ' . ($delay / 60) . ' hours'); }
Metode Carbon::parse()->floatDiffInMinutes('17:00:00') menghitung durasi mundur menuju jam target secara dinamis. Angka menit ini kemudian dioper ke method chained ->delay(now()->addMinutes($delay)).
Job tersebut tidak akan langsung disambar oleh worker, melainkan diparkir manis di tabel jobs sampai waktu rilisnya tiba. Kamu bebas memodifikasi satuan waktu ini sesuai kebutuhan campaign marketing di backend, baik itu hitungan menit, jam, maupun hari.
Dengan trik arsitektur sederhana ini, ketiadaan parameter jadwal di API FCM bukan lagi masalah buntu. Server kamu tetap enteng, UI dashboard admin responsif tanpa loading lama, dan notifikasi meluncur tepat waktu ke layar pengguna.
