Kurnia Andi Nugroho
0 comment
Estimasi Waktu: ~10 Menit Dari versi: Laravel 12.x Ke versi: Laravel 13.x
Panduan ini merupakan terjemahan dan pengembangan dari dokumentasi resmi upgrade Laravel 13. Setiap perubahan dikategorikan berdasarkan tingkat dampak:
| Tingkat Dampak | Keterangan |
|---|---|
| 🔴 Tinggi | Kemungkinan besar mempengaruhi aplikasi Anda |
| 🟡 Sedang | Mungkin mempengaruhi sebagian aplikasi |
| 🟢 Rendah | Hanya mempengaruhi fitur/kasus tertentu |
| ⚪ Sangat Rendah | Jarang terjadi, biasanya pada implementasi kustom |
Tips: Anda bisa menggunakan Shift untuk mengotomatiskan sebagian besar proses upgrade ini, atau gunakan Laravel Boost (MCP server resmi) dengan perintah
/upgrade-laravel-v13di Claude Code, Cursor, atau editor lain yang mendukung MCP.
Sebelum memulai upgrade, lakukan langkah-langkah berikut:
# 1. Buat backup aplikasi Anda
git add .
git commit -m "chore: backup sebelum upgrade ke Laravel 13"
# 2. Pastikan semua test berjalan (opsional tapi disarankan)
php artisan test
# 3. Periksa versi PHP Anda (Laravel 13 membutuhkan PHP 8.2+)
php -v
🔴 Dampak: Tinggi — Langkah ini wajib dilakukan.
Buka file composer.json di root proyek Anda dan ubah versi paket berikut:
{
"require": {
"laravel/framework": "^13.0",
"laravel/tinker": "^3.0"
},
"require-dev": {
"laravel/boost": "^2.0",
"phpunit/phpunit": "^12.0",
"pestphp/pest": "^4.0"
}
}
Setelah mengubah composer.json, jalankan perintah berikut untuk menginstall dependensi baru:
composer update
Catatan: Jika terjadi konflik dependensi, jalankan
composer update --with-all-dependencies.
Jika Anda menggunakan Laravel Installer CLI untuk membuat proyek baru, update installer agar kompatibel dengan Laravel 13:
Jika diinstall via Composer global:
composer global update laravel/installer
Jika menggunakan Laravel Herd: Update aplikasi Herd ke versi terbaru melalui tampilan antarmukanya.
🔴 Dampak: Tinggi — Perubahan nama middleware dan perilaku baru.
Middleware CSRF di Laravel 13 telah diganti namanya dari VerifyCsrfToken menjadi PreventRequestForgery. Selain itu, middleware ini sekarang menyertakan verifikasi asal permintaan menggunakan header Sec-Fetch-Site.
VerifyCsrfToken dan ValidateCsrfToken masih tersedia sebagai alias yang sudah deprecated, namun sebaiknya segera diperbarui.
Cari semua referensi ke VerifyCsrfToken di proyek Anda dan ganti dengan PreventRequestForgery.
Contoh perubahan di test atau route:
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
// Laravel <= 12.x (lama)
->withoutMiddleware([VerifyCsrfToken::class]);
// Laravel >= 13.x (baru)
->withoutMiddleware([PreventRequestForgery::class]);
Cara mencari referensi lama secara cepat:
grep -r "VerifyCsrfToken" app/ bootstrap/ routes/ tests/
🟢 Dampak: Rendah — Hanya mempengaruhi aplikasi yang mengandalkan nilai default framework.
Di Laravel 13, prefix cache dan Redis default kini menggunakan tanda hubung (-) sebagai pemisah, bukan underscore (_).
// Laravel <= 12.x
Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_';
// Laravel >= 13.x
Str::slug(env('APP_NAME', 'laravel')).'-cache-';
Solusi: Jika aplikasi Anda bergantung pada nilai default ini, tambahkan konfigurasi eksplisit di file .env:
CACHE_PREFIX=nama_aplikasi_cache_
REDIS_PREFIX=nama_aplikasi_
SESSION_COOKIE=nama_aplikasi_session
serializable_classes (Dampak Sedang)🟡 Dampak: Sedang — Perubahan keamanan penting.
Laravel 13 menambahkan opsi serializable_classes di konfigurasi cache dengan nilai default false. Ini meningkatkan keamanan dari serangan PHP deserialization gadget chain jika APP_KEY bocor.
Buka file config/cache.php. Jika opsi ini belum ada, tambahkan:
'serializable_classes' => false,
Jika aplikasi Anda menyimpan objek PHP di cache, Anda perlu mendaftarkan class yang diizinkan secara eksplisit:
'serializable_classes' => [
App\Data\CachedDashboardStats::class,
App\Support\CachedPricingSnapshot::class,
],
Peringatan: Jika sebelumnya Anda menyimpan objek PHP sembarang di cache tanpa daftar class eksplisit, Anda perlu memigrasikan data tersebut ke format non-objek (misalnya array) atau mendaftarkan class-nya secara eksplisit.
upsert dengan MySQL atau MariaDB (Dampak Sedang)🟡 Dampak: Sedang
Laravel 13 kini memvalidasi bahwa argumen uniqueBy pada method upsert tidak boleh kosong. Jika kosong, akan melempar InvalidArgumentException.
// Pastikan uniqueBy selalu diisi
DB::table('users')->upsert(
[['email' => '[email protected]', 'name' => 'Test']],
['email'], // <-- uniqueBy, tidak boleh kosong
['name']
);
Periksa semua penggunaan upsert di proyek Anda:
grep -r "upsert" app/ database/
🟢 Dampak: Rendah
Laravel 13 sekarang mengikutsertakan klausa ORDER BY dan LIMIT pada query DELETE ... JOIN untuk MySQL. Sebelumnya, klausa ini bisa diabaikan secara diam-diam.
Jika mesin database Anda tidak mendukung sintaks ini, mungkin akan muncul QueryException. Periksa query DELETE yang menggunakan JOIN di aplikasi Anda.
Container::call dan Nullable Class Defaults (Dampak Rendah)🟢 Dampak: Rendah
Container::call kini menghormati nilai default nullable untuk parameter class saat tidak ada binding yang ditemukan.
$container->call(function (?Carbon $date = null) {
return $date;
});
// Laravel <= 12.x: mengembalikan instance Carbon
// Laravel >= 13.x: mengembalikan null
Jika logika injeksi method Anda bergantung pada perilaku lama (selalu mendapat instance meski nullable), perbarui kode tersebut.
JobAttempted (Dampak Rendah)🟢 Dampak: Rendah
Property pada event Illuminate\Queue\Events\JobAttempted berubah:
// Laravel <= 12.x (lama)
$event->exceptionOccurred; // boolean
// Laravel >= 13.x (baru)
$event->exception; // objek Exception atau null
Cara mencari listener yang terpengaruh:
grep -r "JobAttempted\|exceptionOccurred" app/Listeners/ app/Providers/
Perbarui listener Anda:
// Sebelum
if ($event->exceptionOccurred) { ... }
// Sesudah
if ($event->exception !== null) { ... }
QueueBusy (Dampak Rendah)🟢 Dampak: Rendah
Property $connection pada event Illuminate\Queue\Events\QueueBusy diganti menjadi $connectionName:
// Laravel <= 12.x
$event->connection;
// Laravel >= 13.x
$event->connectionName;
🟢 Dampak: Rendah
Route dengan domain eksplisit kini diprioritaskan sebelum route tanpa domain dalam pencocokan route. Ini membuat catch-all subdomain route bekerja lebih konsisten.
Jika aplikasi Anda memiliki kombinasi domain dan non-domain route, uji kembali perilaku pencocokan route Anda:
php artisan route:list
⚪ Dampak: Sangat Rendah
Membuat instance model baru saat model tersebut masih dalam proses booting sekarang akan melempar LogicException.
// Tidak lagi diizinkan
protected static function boot()
{
parent::boot();
(new static())->getTable(); // ❌ Akan melempar LogicException
}
Pindahkan logika seperti ini ke luar siklus boot.
🟢 Dampak: Rendah
Nama tabel yang digenerate otomatis untuk polymorphic pivot model kustom kini menggunakan bentuk jamak (plural). Jika Anda bergantung pada nama singular sebelumnya, tambahkan definisi tabel eksplisit di pivot model Anda:
class RoleUser extends MorphPivot
{
protected $table = 'role_user'; // definisikan eksplisit
}
🟢 Dampak: Rendah
Saat collection model Eloquent diserialisasi dan dipulihkan (misalnya dalam queued job), relasi yang di-eager-load kini juga dipulihkan. Jika kode Anda mengasumsikan relasi tidak ada setelah deserialisasi, sesuaikan logika tersebut.
Manager extend Callback Binding (Dampak Rendah)🟢 Dampak: Rendah
Closure yang didaftarkan via manager extend sekarang di-bind ke instance manager. Jika sebelumnya Anda menggunakan $this di dalam closure untuk merujuk ke service provider, gunakan use untuk menangkap nilainya:
// Sebelum (mungkin tidak bekerja seperti yang diharapkan)
$manager->extend('driver', function () {
return $this->someMethod(); // $this dulu = service provider
});
// Sesudah (gunakan use)
$manager->extend('driver', function () use ($serviceProvider) {
return $serviceProvider->someMethod();
});
🟢 Dampak: Rendah
Custom factory untuk UUID, ULID, dan random string kini direset setelah setiap test. Jika test Anda bergantung pada factory yang persisten antar method test, pindahkan setup ke setUp() atau di dalam setiap test:
protected function setUp(): void
{
parent::setUp();
Str::createUuidsUsing(fn () => '...');
}
🟢 Dampak: Rendah
Nama view pagination Bootstrap 3 telah diperbarui:
// Laravel <= 12.x
'pagination::default'
'pagination::simple-default'
// Laravel >= 13.x
'pagination::bootstrap-3'
'pagination::simple-bootstrap-3'
Perbarui referensi langsung ke nama view lama di kode Anda.
🟢 Dampak: Rendah
Laravel 13 menambahkan dependency symfony/polyfill-php85 yang mendefinisikan fungsi global seperti array_first() dan array_last() pada PHP versi di bawah 8.5. Ini bisa konflik dengan helper lama.
Solusi terbaik: Gunakan metode dari Illuminate\Support\Arr:
use Illuminate\Support\Arr;
// Gunakan ini sebagai pengganti array_first()
Arr::first($array, function ($value) {
return /* kondisi */;
});
Setelah semua perubahan diterapkan, jalankan langkah-langkah berikut untuk memastikan aplikasi berjalan dengan benar:
# 1. Bersihkan cache
php artisan config:clear
php artisan cache:clear
php artisan route:clear
php artisan view:clear
# 2. Jalankan seluruh test suite
php artisan test
# 3. Cek apakah ada warning atau error
php artisan about
# 4. Periksa route list
php artisan route:list
Jika semua test lulus dan aplikasi berjalan normal, upgrade Anda berhasil! 🎉
| No | Area | Perubahan | Dampak |
|---|---|---|---|
| 1 | Dependensi | Update versi di composer.json | 🔴 Tinggi |
| 2 | Security | VerifyCsrfToken → PreventRequestForgery | 🔴 Tinggi |
| 3 | Cache | serializable_classes config baru | 🟡 Sedang |
| 4 | Database | upsert wajib isi uniqueBy | 🟡 Sedang |
| 5 | Cache | Prefix berubah dari _ ke - | 🟢 Rendah |
| 6 | Container | Container::call nullable default | 🟢 Rendah |
| 7 | Queue | exceptionOccurred → exception | 🟢 Rendah |
| 8 | Queue | $connection → $connectionName | 🟢 Rendah |
| 9 | Routing | Domain route diprioritaskan | 🟢 Rendah |
| 10 | Eloquent | Polymorphic pivot nama plural | 🟢 Rendah |
| 11 | Eloquent | Eager-loaded relations dipulihkan | 🟢 Rendah |
| 12 | Support | Manager extend binding ke manager | 🟢 Rendah |
| 13 | Support | Str factory direset antar test | 🟢 Rendah |
| 14 | Views | Nama view pagination Bootstrap 3 | 🟢 Rendah |
Panduan ini dibuat berdasarkan dokumentasi resmi Laravel 13 Upgrade Guide. Selalu periksa dokumentasi resmi Laravel untuk informasi terbaru.
Kurnia Andi Nugroho
Web & Mobile App Developer, Laravel, Inertia, Vue.Js, React.Js
Founder of Lagikoding.com Laravel Enthusiast & Web Developer