Setiap permintaan HTTP yang masuk ke aplikasi Laravel dimulai dengan pekerjaan yang sama: membaca semua berkas route, mengevaluasi Route::get() satu per satu, mengompilasi pola URL menjadi regex, dan membangun koleksi route di memori. Pekerjaan ini diulang dari nol di setiap permintaan padahal hasilnya identik selama kode tidak berubah. Perintah route:cache membekukan hasilnya menjadi satu berkas PHP array yang dimuat instan. Penghematannya terlihat kecil pada aplikasi mainan — dan menumpuk besar di aplikasi dengan ratusan route yang menerima permintaan tiap detik. Chapter ini menguji perintahnya secara nyata, membongkar isi berkas bekuannya, dan menghadapi jebakan yang paling sering membuat developer bingung: route yang baru ditulis tidak muncul.

Cara Kerja: Route Didaftarkan Ulang Tiap Permintaan #

Tanpa cache, siklus per permintaan berjalan seperti ini: PHP memuat bootstrap/app.php, lalu withRouting() membaca routes/web.php, routes/console.php, dan route dari setiap paket terpasang. Setiap pemanggilan Route::get() menambah objek Route ke koleksi; pola seperti /post/{slug} dikompilasi menjadi regex; akhirnya request dicocokkan. Semua itu adalah kerja persiapan — bukan kerja aplikasi kita. Route cache memindahkan seluruh persiapan itu ke satu perintah yang dijalankan sekali saat deployment.

Membekukan Route #

Perintahnya satu baris:

bash
php artisan route:cache

Output yang Dihasilkan:

text
INFO  Routes cached successfully.

Hasilnya adalah berkas bootstrap/cache/routes-v7.php. Pada sandbox kita (route / dan /halo plus route bawaan Sanctum) berkas ini berukuran 10.601 byte, dan isinya bukan kode routing melainkan array raksasa yang diserahkan langsung ke router:

php
<?php

app('router')->setCompiledRoutes(
    array (
  'compiled' =>
  array (
    0 => false,
    1 =>
    array (
      '/sanctum/csrf-cookie' =>
      array (
        // ... entri route terkompilasi ...

Baris setCompiledRoutes(...) menggantikan seluruh proses pembacaan berkas: PHP cukup return satu array terverifikasi. Perhatikan bahwa perintah ini juga menghapus cache route lama bila ada, jadi aman dijalankan berulang.

Jebakan Klasik: Route Baru Tidak Muncul #

Inilah skenario yang melahirkan puluhan pertanyaan di forum: developer menambahkan route sambil cache masih aktif, lalu aplikasi bersikeras route itu tidak ada. Kita reproduksi secara nyata — cache aktif, lalu kita tambahkan Route::get('/halo', ...) baru di routes/web.php:

bash
php artisan route:list --path=halo

Output yang Dihasilkan:

text
ERROR  Your application doesn't have any routes matching the given criteria.

Aplikasi membaca koleksi route dari berkas cache, bukan dari routes/web.php — route baru tidak pernah terbaca. Solusinya:

bash
php artisan route:clear
php artisan route:list --path=halo

Output yang Dihasilkan:

text
INFO  Route cache cleared successfully.

  GET|HEAD       halo ...................................... routes/web.php:17

                                                            Showing [1] routes

Setelah route:clear, berkas cache dihapus, route dibaca ulang dari sumbernya, dan /halo muncul. Di lingkungan pengembangan, aturan sederhananya: jangan jalankan route:cache — atau bila terlanjur, jadikan route:clear refleks pertama setiap kali perilaku routing terasa “kuno”.

Closure Route: Dulu Ditolak, Kini Diserialisasi #

Selama bertahun-tahun, dokumentasi Laravel melarang closure di route karena cache route tidak bisa menyimpannya — versi luar akan menolak dengan pesan “Unable to prepare route for serialization. Uses Closure.” Di era Laravel modern hal itu berubah. Route /halo kita memang closure, dan justru route:cache di atas berhasil tanpa protes. Isi berkas cache menjelaskan caranya:

text
'uses' => 'O:55:"Laravel\SerializableClosure\UnsignedSerializableClosure":1:{...
    s:8:"function";s:44:"function () {
    return \view(\'welcome\');
}";...}',

Closure dikemas menjadi SerializableClosure — representasi serial yang bisa dibangun ulang di permintaan berikutnya. Ini tetap ada batas praktisnya: closure yang menangkap objek runtime (koneksi database, instance service) tetap tidak dapat diserialisasi. Action berbentuk string — [PageController::class, 'index'] — tetap bentuk paling eksplisit dan paling tahan banting untuk route yang akan dicache di produksi.

Ukur Jujur: Seberapa Besar Penghematannya? #

Disiplin section ini adalah ukur, jangan asumsi. Kita uji dengan sandbox yang memuat ±300 route (ratusan route controller ditambah route bawaan), mengukur waktu vendor/autoload + bootstrap + satu request:

text
=== ±300 route, TANPA cache ===
bootstrap+request: 114.37 ms
bootstrap+request: 116.96 ms
bootstrap+request: 117.95 ms
=== ±300 route, DENGAN cache ===
bootstrap+request: 113.57 ms
bootstrap+request: 112.58 ms
bootstrap+request: 112.26 ms

Selisihnya hanya 3–5 ms per proses. Angka ini jujur menunjukkan bahwa route cache bukan sulap: pada aplikasi kecil dengan sedikit route, penghematan nyaris tak terlihat. Nilainya muncul dari perkalian — proses PHP-FPM yang lahir per permintaan, ratusan route, dan trafik per detik — ditambah keringanan memori dari regex yang tidak dikompilasi ulang. Prinsipnya sama seperti optimasi deployment yang direkomendasikan Laravel: hampir gratis, jadi jalankan di produksi.

Penempatan di Pipeline Deployment #

Urutan eksekusi di pipeline CI/CD harus menghormati dependensi antar perintah:

bash
composer install --no-dev --optimize-autoloader
php artisan migrate --force
npm run build
php artisan route:cache
    # ... lalu server mulai menerima trafik

Route cache ditempatkan setelah kode terbaru berada di server — bukan di tengah proses deploy. Dan bila pipeline memiliki langkah rollback, langkah itu wajib menjalankan php artisan route:clear sebelum mengembalikan versi lama, agar berkas cache yang tersisa tidak berbicara tentang route yang sudah tidak ada.

Route collection kini beku dan instan. Struktur berikutnya yang dibaca ulang di setiap permintaan adalah konfigurasi — dan di sanalah config:cache bekerja, dengan satu aturan emas yang melanggarnya menimbulkan bug paling senyap di Laravel.