What's New in Laravel 13.39 | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://msaied.com) - [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [About](https://msaied.com#about)

           [  Contact](https://msaied.com#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://msaied.com)
- [ProjectsCase studies](https://msaied.com/projects)
- [ArticlesEngineering notes](https://msaied.com/articles)
- [CertificatesCredentials](https://msaied.com/certificates)
- [AboutHow I work](https://msaied.com#about)
- [ContactGet in touch](https://msaied.com#contact)

  [Start a conversation](https://msaied.com#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://msaied.com)
2. /
3. [Articles](https://msaied.com/articles)
4. /
5. [Laravel](https://msaied.com/articles?category=laravel)
6. /
7. What's New in Laravel 13.39: Scheduled Session GC, Fluent Array Rules, and More

   [Laravel](https://msaied.com/articles?category=laravel) 

 What's New in Laravel 13.39: Scheduled Session GC, Fluent Array Rules, and More
================================================================================

 Laravel 13.39 ships a dedicated session:garbage-collect Artisan command, disableable Eloquent primary keys, job duration on JobAttempted, fluent array size rules, atomic cache writes, and a wave of testing fixes.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://msaied.com#person) Published 27 Oct 2026 · Updated 2 Nov 2026 · 3 min read

ShareCopy linkCopied

 ![What's New in Laravel 13.39: Scheduled Session GC, Fluent Array Rules, and More](https://cdn.msaied.com/813/d766b04c30625291c7a35c33d3b0285e.png) 

  On this page +1. [Laravel 13.39 Released](#laravel-1339-released)
2. [Session Garbage Collection on a Schedule](#session-garbage-collection-on-a-schedule)
3. [Disable the Primary Key on an Eloquent Model](#disable-the-primary-key-on-an-eloquent-model)
4. [Job Duration on the JobAttempted Event](#job-duration-on-the-jobattempted-event)
5. [Fluent Size Methods on Rule::array()](#fluent-size-methods-on-rulearray)
6. [Atomic Config, Route, and Event Cache Writes](#atomic-config-route-and-event-cache-writes)
7. [Testing Improvements](#testing-improvements)
8. [Relationship Create and Restore Fixes](#relationship-create-and-restore-fixes)
9. [Key Takeaways](#key-takeaways)

 Laravel 13.39 Released
----------------------

Laravel v13.39.0 landed on 27 October 2026 with a handful of developer-quality-of-life improvements and several important bug fixes. Here is a breakdown of the highlights.

---

Session Garbage Collection on a Schedule
----------------------------------------

By default, Laravel piggybacks expired-session cleanup onto random incoming requests using the `lottery` setting in `config/session.php`. Andrew Brown's new `session:garbage-collect` Artisan command lets you move that work to a scheduled task instead:

```php
use Illuminate\Support\Facades\Schedule;

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

```

The command respects your `session.lifetime` value. To stop requests from also running the cleanup, set the lottery to `[0, 100]`.

---

Disable the Primary Key on an Eloquent Model
--------------------------------------------

Caleb White extended the `#[Table]` attribute so you can pass `key: false` to remove the primary key entirely:

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

Activity::create(['event' => 'login']);

```

A follow-up from Volodya Kurshudyan also marks these models as non-incrementing, so `create()` no longer throws after the insert.

---

Job Duration on the JobAttempted Event
--------------------------------------

Jack Bayliss added a `duration` property (in milliseconds) to `JobAttempted`. Previously only `JobProcessed` carried timing data, leaving failed or released jobs without it:

```php
use Illuminate\Queue\Events\JobAttempted;

Event::listen(function (JobAttempted $event) {
    if (! $event->successful()) {
        Log::warning('Job failed', [
            'job'         => $event->job->resolveName(),
            'duration_ms' => $event->duration,
        ]);
    }
});

```

A second PR extends the same timing to the sync queue, so `dispatchSync()` jobs now report duration on both events.

---

Fluent Size Methods on Rule::array()
------------------------------------

Richard van Baarsen added `min()`, `max()`, `between()`, and `exactly()` to `Rule::array()`, bringing it in line with `Rule::string()`:

```php
// Before
'photos' => [Rule::required(), Rule::array(), 'min:1'],

// After
'photos' => [Rule::required(), Rule::array()->min(1)->max(10)],

```

The methods also compose with allowed-key lists: `Rule::array(['a', 'b'])->min(1)` produces `array:a,b|min:1`.

---

Atomic Config, Route, and Event Cache Writes
--------------------------------------------

The `config:cache`, `route:cache`, and `event:cache` commands now write to a temporary file and rename it atomically. Previously, a request that booted mid-write could read an empty cache file. With `opcache.validate_timestamps=0`, OPcache would keep that empty file cached until PHP-FPM restarted — causing every subsequent request to fail.

---

Testing Improvements
--------------------

Several fixes landed for the testing layer:

- **`#[WithoutMigrationEvents]`** — stops migration queries from reaching `DB::listen()` and `expectsDatabaseQueryCount()` when `LazilyRefreshDatabase` runs during a test.
- **Mail fake** now intercepts `Mail::html()` and `Mail::plain()`, preventing real emails from being sent in tests.
- **`BusFake::withoutDispatchingAfterResponses()`** — lets after-response jobs execute while other jobs remain faked.
- **`RefreshDatabase`** rolls back every open transaction level after each test, so a leaked transaction no longer breaks subsequent tests.
- **`assertSeeText('0')`** now correctly fails when the string `'0'` is absent from the response.

---

Relationship Create and Restore Fixes
-------------------------------------

`BelongsToMany` gains `forceCreate()` and `forceCreateQuietly()`. The `restoreOrCreate()` and `createOrRestore()` methods now run through the relationship instead of the query builder, fixing missing foreign keys on has-many and wrong-model restores on belongs-to-many. `HasManyThrough` and `HasOneThrough` are also covered.

---

Key Takeaways
-------------

- Move session GC off the request path with `session:garbage-collect` on a schedule.
- Use `#[Table(key: false)]` for keyless Eloquent models without extra property overrides.
- Track failed-job timing with `JobAttempted::$duration`.
- Replace string size rules with `Rule::array()->min()->max()` for cleaner validation.
- Atomic cache writes eliminate the OPcache empty-file race condition.
- Several testing helpers now behave correctly in edge cases.

---

Source: [Laravel News — Scheduled Session Garbage Collection in Laravel 13.39](https://laravel-news.com/laravel-13-39-0)

- [Laravel 13](https://msaied.com/articles?search=Laravel%2013)
- [session management](https://msaied.com/articles?search=session%20management)
- [Eloquent](https://msaied.com/articles?search=Eloquent)
- [queue](https://msaied.com/articles?search=queue)
- [validation](https://msaied.com/articles?search=validation)
- [release](https://msaied.com/articles?search=release)

 Frequently asked questions 
---------------------------

  How do I stop Laravel from running session garbage collection on random requests?Set the lottery option in config/session.php to \[0, 100\] and then schedule the new session:garbage-collect Artisan command to run as often as you need, for example hourly.

   Why did config:cache and route:cache sometimes break all requests when OPcache was enabled?Before Laravel 13.39, those commands wrote directly to the cache file. A request that booted mid-write could read an empty file, and with opcache.validate\_timestamps=0 OPcache kept that empty file until PHP-FPM restarted. The fix writes to a temporary file first and then renames it atomically.

   How can I measure how long a failed or released queue job took to run?Listen to the JobAttempted event added in Laravel 13.39. It now carries a duration property in milliseconds, available for both successful and unsuccessful job attempts, including jobs run with dispatchSync().

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://msaied.com#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://msaied.com#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleFilament v5.8: Deferred Schema Loading, Table Grouping Improvements, and More](https://msaied.com/articles/filament-v58-deferred-schema-loading-table-grouping-improvements-and-more) [Next articlePollora: Run WordPress Inside a Laravel Application](https://msaied.com/articles/pollora-run-wordpress-inside-a-laravel-application)  

   On this page
-------------

1. [Laravel 13.39 Released](#laravel-1339-released)
2. [Session Garbage Collection on a Schedule](#session-garbage-collection-on-a-schedule)
3. [Disable the Primary Key on an Eloquent Model](#disable-the-primary-key-on-an-eloquent-model)
4. [Job Duration on the JobAttempted Event](#job-duration-on-the-jobattempted-event)
5. [Fluent Size Methods on Rule::array()](#fluent-size-methods-on-rulearray)
6. [Atomic Config, Route, and Event Cache Writes](#atomic-config-route-and-event-cache-writes)
7. [Testing Improvements](#testing-improvements)
8. [Relationship Create and Restore Fixes](#relationship-create-and-restore-fixes)
9. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://msaied.com#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/812/aa4ad01a8367beed63fe5ee9c09ef940.png) Laravel · 4 min read### Pollora: Run WordPress Inside a Laravel Application

29 Oct 2026 ](https://msaied.com/articles/pollora-run-wordpress-inside-a-laravel-application) [ ![](https://cdn.msaied.com/808/d118ca0a73009ac41175d54bc274a4d5.png) Laravel · 4 min read### Filament v5.8: Deferred Schema Loading, Table Grouping Improvements, and More

26 Oct 2026 ](https://msaied.com/articles/filament-v58-deferred-schema-loading-table-grouping-improvements-and-more) [ ![](https://cdn.msaied.com/811/06569a963be3a67e056761798af93374.png) Filament · 4 min read### Filament v4.16.0 Released: Security Hardening, Lazy Loading APIs &amp; More

26 Oct 2026 ](https://msaied.com/articles/filament-v4160-released-security-hardening-lazy-loading-apis-more) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://msaied.com#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://msaied.com)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://msaied.com/sitemap.xml)
