โ† Semua pembelajaran / Laravel Nol โ†’ Enterprise
Fase 1 ยท Request, Routing & Blade

Middleware

Middleware adalah rangkaian bawang di sekitar controller-mu. Memahami arah alirannya menjelaskan kenapa ada kode yang jalan sebelum controller dan ada yang setelah respons terkirim.

Sumber asli laravel.com Resmi Rangkuman ~7 menit baca

Intisari

  • Bentuknya satu method handle($request, Closure $next). Kode sebelum $next berjalan saat masuk, setelahnya saat keluar.
  • Sejak Laravel 11 middleware didaftarkan di bootstrap/app.php, bukan di app/Http/Kernel.php yang sudah tidak ada.
  • Tiga cakupan: global, grup (web/api), dan per-rute lewat alias.
  • Laravel 13 menambahkan atribut #[Middleware] supaya bisa ditempel langsung di controller.
  • terminate() berjalan setelah respons dikirim ke pengguna โ€” tempat yang tepat untuk pekerjaan ringan pasca-respons.

Bentuk dan arah aliran

// app/Http/Middleware/PastikanLangganganAktif.php
class PastikanLangganganAktif
{
    public function handle(Request $request, Closure $next): Response
    {
        // โ”€โ”€ MASUK: berjalan sebelum controller
        if (! $request->user()?->langgananAktif()) {
            return redirect()->route('langganan.bayar');   // hentikan di sini
        }

        $response = $next($request);        // โ† controller (dan middleware lain) berjalan di baris ini

        // โ”€โ”€ KELUAR: berjalan setelah controller, sebelum respons dikirim
        $response->headers->set('X-Langganan', 'aktif');

        return $response;
    }
}
permintaan  โ†’  A masuk  โ†’  B masuk  โ†’  CONTROLLER  โ†’  B keluar  โ†’  A keluar  โ†’  respons

Urutan keluar adalah kebalikan urutan masuk. Middleware yang didaftarkan paling awal adalah yang paling luar: ia melihat permintaan lebih dulu dan menyentuh respons paling akhir.

Mendaftarkan โ€” tiga cakupan

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {

    // 1. Global โ€” setiap permintaan, tanpa kecuali
    $middleware->append(PaksaHttps::class);

    // 2. Per grup โ€” hanya rute web, atau hanya rute api
    $middleware->web(append: [CatatKunjungan::class]);
    $middleware->api(prepend: [PastikanJson::class]);

    // 3. Alias โ€” dipakai per rute
    $middleware->alias([
        'langganan' => PastikanLangganganAktif::class,
    ]);
})
// routes/web.php
Route::get('/laporan', [LaporanController::class, 'index'])
    ->middleware(['auth', 'langganan']);

Sejak Laravel 13, middleware juga bisa dinyatakan langsung di controller lewat atribut:

use Illuminate\Routing\Attributes\Controllers\Middleware;

#[Middleware('auth')]
class LaporanController
{
    #[Middleware('langganan')]
    public function bulanan() { /* ... */ }
}

Kapan memilih yang mana. Aturan praktisnya: kalau syaratnya berlaku untuk seluruh aplikasi, pakai global; kalau berlaku untuk sekelompok rute yang sudah punya nama (admin, api), pakai grup; kalau berlaku untuk beberapa aksi tertentu, pakai atribut atau alias di rute. Middleware global yang sebenarnya hanya relevan untuk tiga rute adalah pemborosan yang terasa saat lalu lintas naik.

Middleware dengan parameter

public function handle(Request $request, Closure $next, string ...$peran): Response
{
    if (! $request->user()->hasAnyRole($peran)) {
        abort(403);
    }

    return $next($request);
}
Route::get('/admin', ...)->middleware('peran:admin,supervisor');

terminate() โ€” setelah pengguna menerima respons

class CatatDurasi
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        // Pengguna sudah menerima halamannya. Ini tidak menambah waktu tunggunya.
        Log::info('durasi', [
            'rute'   => $request->route()?->getName(),
            'status' => $response->getStatusCode(),
            'ms'     => round((microtime(true) - LARAVEL_START) * 1000),
        ]);
    }
}

Peringatan untuk Fase 8. terminate() memang berjalan setelah respons terkirim, tapi di worker mode ia tetap menahan worker tersebut sampai selesai. Di PHP-FPM, proses itu memang mau mati; di worker mode, ia seharusnya sudah siap melayani permintaan berikutnya. Pekerjaan berat tetap harus masuk antrean, bukan ke terminate().

Middleware bawaan yang perlu kamu kenal

AliasGunanya
authWajib login; kalau tidak, lempar ke halaman login
auth:sanctumWajib token API yang sah (Fase 4)
verifiedWajib email terverifikasi
throttle:60,1Maksimum 60 permintaan per menit (Fase 4)
signedURL bertanda tangan โ€” untuk tautan sekali pakai
can:update,produkJalankan policy sebelum controller (Fase 4)

Latihan: buat middleware CatatDurasi di atas, pasang sebagai global, lalu tambahkan Log::info('masuk') sebelum $next dan Log::info('keluar') sesudahnya di dua middleware berbeda. Muat satu halaman, lalu baca storage/logs/laravel.log dan pastikan urutannya benar-benar seperti diagram bawang di atas.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.