Tutorial Laravel 12: Menggunakan Context Facade untuk Propagasi Data
Laravel 12 nambah fitur baru namanya Context. Kalau kamu pernah bingung gimana caranya nyebarin data kayak tenant_id, request_id, atau user_id ke semua log, job, dan event tanpa harus ngoper variabel ke mana-mana — ini solusinya.
Tutorial ini bahas Context dari dasar sampai implementasi nyata. Cocok buat kamu yang udah familiar sama Laravel tapi belum pernah pakai Context facade.
Apa Itu Context di Laravel 12?
Context adalah cara Laravel nyimpen data yang terikat sama satu request atau satu eksekusi. Data ini bisa diakses dari mana aja tanpa harus ngoper variabel ke tiap fungsi atau class.
Bayangin kamu punya aplikasi multi-tenant. Setiap request butuh tahu tenant_id-nya siapa. Biasanya kamu harus:
- Ngirim
$tenantIdke controller - Terus ke service
- Terus ke repository
- Terus ke job
- Terus ke log
Ribet. Context bikin ini jadi simpel:
use Illuminate\Support\Facades\Context;
// Set sekali di middleware
Context::add('tenant_id', 123);
// Ambil di mana aja
$tenantId = Context::get('tenant_id'); // 123
Data ini otomatis ikut ke log, job, event, dan exception yang terjadi dalam request yang sama.
Cara Pakai Context: Dasar
Context tersedia lewat facade Illuminate\Support\Facades\Context atau helper context().
Menambah Data ke Context
Context::add('request_id', 'REQ-20260525-0001');
Context::add('user_id', 42);
// Atau sekaligus
Context::add([
'request_id' => 'REQ-20260525-0001',
'user_id' => 42,
]);
Mengambil Data dari Context
$requestId = Context::get('request_id');
// Dengan default value kalau tidak ada
$userId = Context::get('user_id', 0);
Menghapus Data dari Context
// Hapus satu key
Context::forget('request_id');
// Hapus semua
Context::flush();
Studi Kasus 1: Request ID di Semua Log
Masalah klasik: saat ada error, susah tahu log mana yang terkait satu request. Apalagi kalau traffic tinggi, log campur aduk.
Solusinya: bikin middleware yang nambah request ID ke Context.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
class AddRequestContext
{
public function handle(Request $request, Closure $next): mixed
{
Context::add([
'request_id' => Str::uuid()->toString(),
'url' => $request->fullUrl(),
'method' => $request->method(),
'ip' => $request->ip(),
'user_id' => $request->user()?->id,
]);
return $next($request);
}
}
Daftarkan di bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->prepend(AddRequestContext::class);
})
Sekarang semua log otomatis punya context:
Log::info('Memproses pembayaran');
// Output di log:
// [2026-05-25 10:23:45] local.INFO: Memproses pembayaran
// {"request_id":"abc-123","url":"/checkout","user_id":42}
Kalau ada error, tinggal cari semua log dengan request_id yang sama. Debugging jadi jauh lebih gampang.
Studi Kasus 2: Multi-Tenancy dengan Context
Aplikasi multi-tenant butuh tenant_id di hampir semua query. Biasanya kamu harus ngirim $tenantId ke mana-mana. Dengan Context, cukup set sekali di middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Context;
class ResolveTenant
{
public function handle(Request $request, Closure $next): mixed
{
// Resolve tenant dari subdomain atau header
$tenant = $this->getTenantFromRequest($request);
Context::add('tenant_id', $tenant->id);
return $next($request);
}
private function getTenantFromRequest(Request $request)
{
// Contoh: dari subdomain
$subdomain = explode('.', $request->getHost())[0];
return Tenant::where('subdomain', $subdomain)->firstOrFail();
}
}
Sekarang di service atau repository, tinggal ambil dari Context:
class OrderService
{
public function getCurrentTenantOrders()
{
$tenantId = Context::get('tenant_id');
return Order::where('tenant_id', $tenantId)->get();
}
}
Catatan penting: Jangan bergantung 100% pada Context untuk data bisnis kritis. Kalau kamu jalanin Artisan command atau test, Context mungkin kosong. Selalu kasih default value atau cek null.
Studi Kasus 3: Propagasi Context ke Queue Jobs
Masalah: ketika request trigger job di queue, context (request ID, user ID) hilang karena job jalan di worker terpisah.
Kabar baiknya: Context otomatis dipropagasi ke jobs kalau job implement ShouldQueue.
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\Log;
class ProcessPayment implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function __construct(
private int $orderId
) {}
public function handle(): void
{
// Context dari request yang dispatch job ini otomatis tersedia
$requestId = Context::get('request_id');
Log::info("Memproses pembayaran order {$this->orderId}", [
'request_id' => $requestId,
]);
// proses pembayaran...
}
}
Best practice: Untuk data penting kayak tenant_id, simpan juga sebagai property Job. Jangan cuma andalin Context, karena lifecycle worker bisa beda-beda.
Studi Kasus 4: Hidden Context untuk Data Sensitif
Kadang kamu butuh data di Context tapi nggak mau muncul di log (misalnya token atau API key).
// Tambah context tersembunyi — tidak masuk ke log
Context::addHidden([
'payment_token' => $request->payment_token,
'api_key' => config('services.payment.key'),
]);
// Ambil di bagian lain
$token = Context::getHidden('payment_token');
Data ini tetap bisa diakses di kode, tapi nggak akan muncul di log output.
Kapan Pakai Context vs Dependency Injection?
Context bukan pengganti dependency injection. Ini panduan kapan pakai mana:
Pakai Context kalau:
- Data perlu tersebar ke banyak layer (log, job, event) tanpa ubah signature
- Multi-tenant:
tenant_idbutuh ada di mana-mana - Request tracing:
request_iduntuk debugging - Feature flags: tracking fitur apa yang aktif per request
Pakai Dependency Injection kalau:
- Data bisnis yang jelas dependensinya (misal
OrderServicebutuhPaymentGateway) - Konfigurasi statis (cukup lewat config atau env)
- Kode yang perlu di-test dengan mock/stub
Intinya: Context untuk data “ambient” yang perlu ada di mana-mana. DI untuk dependensi eksplisit yang jelas.
Testing Kode yang Pakai Context
Di test, kamu harus set Context secara manual:
use Illuminate\Support\Facades\Context;
public function test_service_menggunakan_tenant_dari_context()
{
Context::flush(); // Pastikan kosong dulu
Context::add('tenant_id', 10);
$service = app(OrderService::class);
$orders = $service->getCurrentTenantOrders();
$this->assertTrue(
$orders->every(fn ($order) => $order->tenant_id === 10)
);
}
Jangan lupa Context::flush() di setUp() atau tearDown() biar nggak ada kebocoran state antar test.
Pitfall yang Harus Dihindari
1. Overuse Context = Global State
Kalau semua data masuk Context, kode jadi susah di-trace. Pakai Context cuma untuk data yang emang perlu ada di mana-mana.
2. Lupa Guard di CLI/Artisan
Artisan command jalan tanpa request, jadi Context bisa kosong. Selalu kasih default:
$tenantId = Context::get('tenant_id');
if (is_null($tenantId)) {
throw new \RuntimeException('Tenant ID tidak tersedia di context');
}
3. Ubah Context di Layer Bawah
Kalau middleware A set tenant_id, jangan ubah lagi di service atau repository. Bikin aturan: Context cuma di-set di middleware, layer lain cuma baca.
Ringkasan
Context facade di Laravel 12 bikin kamu bisa nyebarin data ke seluruh aplikasi tanpa harus ngoper variabel ke mana-mana. Cocok buat:
- Request tracing dengan
request_id - Multi-tenancy dengan
tenant_id - Feature flags dan debugging
- Propagasi data ke log, job, dan event
Tapi ingat: jangan overuse. Pakai Context untuk data ambient, pakai DI untuk dependensi eksplisit.
Baca Juga
- Tutorial Laravel 12: Job Batching untuk Proses Batch yang Efisien
- Cara Install Laravel 12 untuk Pemula
- Cara Membuat Controller di Laravel 12
Butuh tim yang bantu setup observability dan logging yang proper di aplikasi Laravel? Lihat layanan pengembangan aplikasi kami.
Artikel Lainnya di Kategori Laravel
10 November 2025
Cara Membuat Custom Artisan Command di Laravel 12
Tutorial step-by-step membuat custom artisan command di Laravel 12: php artisan make:command, handle method, dan best practices.
Baca Artikel10 November 2025
Contoh Penggunaan Contract di Laravel 12: Implementasi dan Binding
Kalau Anda sudah membaca artikel tentang apa itu Contract di Laravel 12, artikel ini melanjutkannya dengan contoh penggunaan nyata: bagaimana membuat implementasi Contract sendiri dan kapan ini berguna dalam proyek. Menggunakan Contract Bawaan Laravel Contract bawaan Laravel ada di namespace Illuminate\Contracts\*. Contoh yang paling sering dipakai adalah type-hinting di constructor untuk decoupling: <?php namespace App\Services; […]
Baca Artikel10 November 2025
Apa Itu Observer di Laravel 12 dan Kapan Menggunakannya
Kalau Event dan Listener cocok untuk “sesuatu terjadi di aplikasi, beri tahu komponen lain”, Observer punya fokus berbeda: “ketika Eloquent model berubah, jalankan kode ini.” Artikel ini menjelaskan apa itu Observer di Laravel 12, perbedaannya dengan Event Listener, dan kapan sebaiknya dipakai. Apa Itu Observer? Observer adalah kelas yang merespons event lifecycle Eloquent model: saat […]
Baca ArtikelIngin Membaca Artikel Lainnya?
Temukan lebih banyak insight dan tips tentang teknologi dan bisnis digital.
Lihat Semua Artikel