Gratis update & dukungan instalasi

News 28 Oct 2026

Laravel 13.39.0 Rilis 27 Oktober 2026: Model Tanpa Primary Key, restoreOrCreate & Dampaknya untuk Source Code Kamu

R Oleh renz mobellgnd
Laravel 13.39.0 Rilis 27 Oktober 2026: Model Tanpa Primary Key, restoreOrCreate & Dampaknya untuk Source Code Kamu

Laravel 13.39.0 rilis 27 Oktober 2026 sebagai rilis mingguan tanpa breaking change: model tanpa primary key lewat #[Table(key: false)], restoreOrCreate() yang menghormati relasi Eloquent, dan session:garbage-collect terjadwal. Untuk source code toko online dan aplikasi kasir, update ini aman diambil lewat composer update, asal diuji di staging dulu.

Apa itu Laravel 13.39.0?

Laravel 13.39.0 adalah rilis minor mingguan lini Laravel 13, yang pertama rilis 17 Maret 2026 dan mensyaratkan PHP 8.3 minimum. Berbeda dengan rilis mayor, rilis mingguan seperti ini tidak mengubah perilaku lama secara diam-diam: semua fitur barunya bersifat opt-in.

Fakta kunci:

Fakta Keterangan
VersiLaravel 13.39.0
Tanggal rilis27 Oktober 2026
Breaking changeTidak ada
PHP yang didukung8.3–8.5
Dukungan bug fix Laravel 13Sampai Q3 2027
Dukungan keamanan Laravel 13Sampai 17 Maret 2028
Cara updatecomposer update laravel/framework

Seminggu sebelumnya, Laravel 13.38.0 rilis 20 Oktober 2026 membawa middleware anti stale read; 13.39 melanjutkan pola itu.

Apa yang baru di Laravel 13.39.0?

1. Model tanpa primary key: #[Table(key: false)]

Tidak semua tabel punya primary key: tabel log aktivitas yang hanya ditulis (append-only), tabel staging hasil impor, atau view database. Sebelum 13.39, Anda harus menulis protected $primaryKey = null; plus public $incrementing = false;, dan itupun masih bisa bermasalah. Sejak Laravel 13 model bisa memakai atribut #[Table], tetapi key: null ternyata berarti "pakai default" (id), bukan "tidak ada key". Di 13.39, key: false sungguh berarti tidak ada key, dan incrementing ikut dimatikan sehingga create() menyimpan baris tanpa error:

use Illuminate\Database\Eloquent\Attributes\Table;
use Illuminate\Database\Eloquent\Model;

#[Table(name: 'activity_log', key: false)]
class Activity extends Model
{
//
}

Untuk source code toko online, tabel activity_log atau import_staging kini cukup dimodelkan dengan satu baris atribut. Batasannya harus jujur: model tanpa key tidak bisa dipakai untuk find(), update pada instance yang sudah dimuat, delete pada instance, maupun route model binding — semua itu butuh key. Model keyless hanya untuk menulis baris dan menanyakannya lewat where().

2. Bagaimana cara kerja restoreOrCreate() yang kini menghormati relasi?

Ini perbaikan paling berdampak untuk aplikasi dengan soft delete — dan hampir semua toko online memakainya, misalnya alamat pelanggan yang dihapus lalu ditambahkan lagi.

Kasus nyatanya: pelanggan menghapus alamat berlabel "Rumah", lalu menambahkannya kembali. Anda memanggil $user->addresses()->restoreOrCreate(['label' => 'Rumah']) dan berharap baris lama pelanggan itu yang kembali. Sebelum 13.39, pemanggilan pada relasi melewati relasi sepenuhnya: Laravel mencari alamat "Rumah" mana pun, dan kalau harus membuat baru, user_id-nya tidak terisi. Pada belongsToMany, id pivot bahkan bisa menimpa kunci relasi sehingga baris milik orang lain yang ter-restore; pada relasi through, id tabel perantara dari join bisa membuat baris milik parent lain yang kembali.

Di 13.39, keduanya menjadi metode nyata pada HasOneOrMany, BelongsToMany, dan relasi through. Alurnya tetap di dalam relasi: query relasi ditambah withTrashed(), lalu memakai firstOrCreate() atau createOrFirst() milik relasi itu sendiri:

$request->user()->addresses()->restoreOrCreate(
['label' => $request->validated('label')],
$request->safe()->except('label'),
);

Baris soft-deleted milik user itu di-restore (deleted_at dikosongkan); kalau tidak ada, baris baru dibuat lewat relasi dengan user_id terisi benar. Pilih createOrRestore() bila kolomnya punya unique index dan Anda mengantisipasi race condition: metode ini mencoba insert dulu, lalu mencari dan me-restore saat terjadi pelanggaran unique constraint.

Pola alamat dan relasi seperti ini adalah fondasi keranjang belanja dan checkout Laravel yang anti double order. Kalau Anda menjual source code toko, perbaikan ini layak disebut di changelog produk Anda.

3. forceCreate() pada belongsToMany akhirnya me-attach model

Sebelumnya BelongsToMany tidak punya forceCreate(), sehingga panggilan itu jatuh ke Eloquent builder: model tersimpan tetapi tidak pernah di-attach, dan $team->users()->count() tetap 0. Di 13.39, $user->teams()->forceCreate() menyimpan model sekaligus me-attach-nya:

$team = $user->teams()->forceCreate(
['name' => 'Core', 'is_internal' => true],
['role' => 'owner'],
);

4. Aturan ukuran array kini fluent: Rule::array()->min()->max()

Sejak 13.36 hampir semua rule validasi punya builder fluent, tetapi ukuran array masih butuh string terpisah seperti Rule::array(), 'min:1'. Di 13.39, Rule::array() mendapat min(), max(), between(), dan exactly() (dikompilasi menjadi size):

'tags' => ['required', Rule::array()->min(1)->max(5)],
'cover' => ['nullable', Rule::array(['url', 'alt'])->exactly(2)],

Hasil kompilasinya tetap string rule biasa, sehingga pesan error ke form Vue/Inertia tidak berubah — murni soal keterbacaan.

5. Mengapa session:garbage-collect penting untuk shared hosting?

Dengan driver session file atau database, Laravel menghapus session kedaluwarsa lewat lotre: 2 dari setiap 100 request ('lottery' => [2, 100] di config/session.php). Request yang "kalah undian" menanggung biaya bersih-bersih, situs sepi nyaris tidak pernah bersih-bersih, situs ramai justru terlalu sering. Perintah baru menjalankan garbage collector driver sesuai session.lifetime kapan pun Anda mau:

use Illuminate\Support\Facades\Schedule;

Schedule::command('session:garbage-collect')->hourly();

Pasangkan dengan 'lottery' => [0, 100] agar request tidak lagi ikut bersih-bersih. Untuk Redis/Memcached, perintah ini no-op. Bagi pengguna shared hosting — lihat panduan setting cron job Laravel di shared hosting — satu baris scheduler ini menggantikan lotre acak.

6. Durasi job kini tercatat juga untuk job yang gagal

Sebelumnya hanya JobProcessed job sukses yang membawa $duration. JobAttempted — yang dipicu setiap percobaan, sukses maupun gagal — kini ikut membawa $duration (float, milidetik), termasuk untuk queue sync:

use Illuminate\Queue\Events\JobAttempted;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;

Event::listen(function (JobAttempted $event) {
if (! $event->successful() && $event->duration > 10_000) {
Log::warning('Slow failing job', [
'job' => $event->job->resolveName(),
'ms' => $event->duration,
]);
}
});

Perbaikan apa lagi yang dibawa 13.39.0?

Enam peningkatan testing yang menghilangkan flaky test atau email sungguhan dari CI:

  1. #[WithoutMigrationEvents] — mengecualikan query migrasi dari hitungan expectsDatabaseQueryCount().
  2. RefreshDatabase mengembalikan semua level transaksi — test yang meninggalkan transaksi terbuka tak lagi merusak test sesudahnya.
  3. Bus::fake() + withoutDispatchingAfterResponses() — sebagian job bisa di-fake sementara job dispatchAfterResponse() asli tetap berjalan.
  4. Mail::html() dan Mail::plain() ikut di-fake — sebelumnya keduanya diteruskan ke mailer asli, sehingga email sungguhan terkirim sementara assertNothingOutgoing() tetap lolos.
  5. Shorthand Process::fake() untuk start() dan pool() — berlaku juga untuk proses asinkron.
  6. assertSeeText('0') tidak lagi lolos saat teks tidak ada — test yang mulai gagal kemungkinan memang salah sejak awal.

Dua perbaikan deploy yang diam tetapi nyata: config:cache, route:cache, dan event:cache kini ditulis atomik (file sementara lalu rename), sehingga request yang boot tepat saat deploy tidak lagi membaca file config kosong. Dan Filesystem::exists() serta isFile() kini memblokir skema phar:// karena path phar:// dapat memicu deserialisasi objek.

Bagaimana cara update ke Laravel 13.39.0 dengan aman?

Ikuti urutan ini, dan jangan lewati staging:

  1. Cek versi Anda sekarang dengan composer show laravel/framework dan pastikan PHP 8.3+ (php -v).
  2. Cadangkan dulu database dan file — lihat panduan backup otomatis website Laravel.
  3. Update di branch terpisah, bukan langsung di main:
composer update laravel/framework
php artisan config:clear
php artisan cache:clear
  1. Jalankan test suite, dengan perhatian khusus pada test restoreOrCreate() pada relasi, Mail::fake(), atau expectsDatabaseQueryCount() — ketiganya berubah perilaku.
  2. Uji skenario soft delete secara manual: hapus alamat pelanggan, tambahkan lagi dengan label sama, dan pastikan baris yang kembali adalah milik pelanggan itu.
  3. Baru deploy, dan jalankan php artisan optimize setelah deploy — berkat penulisan cache atomik, langkah ini kini lebih aman.

Bila Anda masih di Laravel 12, jangan buru-buru: patch keamanan tersedia sampai 24 Februari 2027, tetapi bug fix sudah berhenti sejak 13 Agustus 2026. Panduan upgrade Laravel ke versi terbaru menjelaskan urutan aman dari versi 8/9/10/11.

Apakah Laravel 13.39.0 wajib segera diupdate?

Untuk sebagian besar aplikasi Laravel 13, jawabannya: ya, pada jadwal update rutin berikutnya — bukan tengah malam ini juga. Rilis ini tidak menambal celah keamanan kritis, jadi tidak ada urgensi darurat. Tetapi tiga kelompok sebaiknya memprioritaskannya: aplikasi yang memakai restoreOrCreate() pada relasi, aplikasi dengan session driver file/database di shared hosting, dan tim yang CI-nya memakai Mail::fake().

Sebaliknya, tunda dulu bila Anda masih mengandalkan perilaku lama restoreOrCreate(), atau bila test suite Anda belum pernah hijau. Perbaiki baseline dulu, baru update.

Kesimpulan

Laravel 13.39.0 adalah contoh rilis mingguan yang baik: tidak berisik, tidak merusak, tetapi menyelesaikan bug yang benar-benar ditemui di produksi — relasi soft delete yang salah restore, model tanpa key yang setengah jalan, session yang dibersihkan lewat lotre, dan cache deploy yang tidak atomik. Bagi penjual source code, nilainya ganda: perbaikan gratis lewat composer update, plus bahan changelog yang meyakinkan pembeli bahwa produk Anda dirawat mengikuti frameworknya.

FAQ

Apa itu Laravel 13.39.0?

Rilis minor mingguan lini Laravel 13 yang terbit 27 Oktober 2026, tanpa breaking change: model keyless, perbaikan restoreOrCreate() pada relasi, session:garbage-collect, durasi job gagal, dan perbaikan testing serta deploy.

Apakah update ke Laravel 13.39.0 aman?

Ya, untuk aplikasi Laravel 13 yang sehat: tanpa breaking change dan semua fitur baru opt-in. Tetap update di branch terpisah, jalankan test suite, dan uji di staging — terutama bila memakai restoreOrCreate() pada relasi atau Mail::fake().

Bagaimana cara update ke Laravel 13.39.0?

Jalankan composer update laravel/framework, lalu php artisan config:clear dan php artisan cache:clear. Pastikan PHP 8.3–8.5, karena Laravel 13 mensyaratkan PHP 8.3 minimum.

Apa fungsi #[Table(key: false)] di Laravel 13.39?

Atribut ini menyatakan model tidak punya primary key — cocok untuk tabel log append-only, tabel staging impor, atau view database. Berbeda dengan key: null, key: false benar-benar berarti "tidak ada key" dan mematikan incrementing sehingga create() berhasil.

Mengapa restoreOrCreate() pada relasi penting diperbaiki?

Sebelum 13.39, pemanggilan pada relasi mengabaikan relasi: baris baru bisa kehilangan foreign key, dan pada belongsToMany maupun relasi through, baris milik parent lain bisa ikut ter-restore. Setelah perbaikan, pencarian dan pembuatan tetap di dalam relasi.

Apakah session:garbage-collect menggantikan lotre session?

Ya, untuk driver file dan database: jadwalkan Schedule::command('session:garbage-collect')->hourly() dan set 'lottery' => [0, 100]. Untuk Redis/Memcached, perintah ini tidak berpengaruh karena session kedaluwarsa sendiri.

Sumber: dokumentasi resmi Laravel (laravel.com/docs/releases) dan changelog framework per 27 Oktober 2026.