News

Debounceable Queued Jobs in Laravel 13.6.0

Published
Debounceable Queued Jobs in Laravel 13.6.0 image

Release updates

Never miss a Laravel release

Get an email whenever a new Laravel release lands.

Laravel v13.6.0 introduces debounceable queued jobs — when the same job is dispatched multiple times within a time window, only the last dispatch executes. This release also adds JSON response support for the built-in health route, a new JsonFormatter for logging, Cloudflare Email Service integration, and several internal improvements.

  • Debounceable queued jobs with #[DebounceFor] attribute
  • JSON responses for the built-in health route (/up)
  • New JsonFormatter for structured JSON logging
  • Cloudflare Email Service support
  • Array of pivot arrays support for hasAttached
  • Improved model assertions in testing
  • Various bug fixes and performance improvements

What's New

Debounceable Queued Jobs

This release adds debouncing support for queued jobs. When the same job is dispatched multiple times within a time window, only the last dispatch executes — earlier dispatches are silently discarded at execution time.

Apply the #[DebounceFor] attribute to any ShouldQueue job:

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\DebounceFor;
 
#[DebounceFor(30)]
class RebuildSearchIndex implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
 
public function __construct(public int $documentId) {}
 
public function debounceId(): string
{
return (string) $this->documentId;
}
 
public function handle(): void
{
SearchIndex::rebuild($this->documentId);
}
}
 
// User edits a document 10 times in 30 seconds — only the last dispatch runs
RebuildSearchIndex::dispatch($document->id);

Debouncing can also be applied at the dispatch site without modifying the job class:

dispatch(new SyncExternalData($accountId))->debounceFor(30);

A maxWait parameter caps how long a job can be deferred:

#[DebounceFor(30, maxWait: 120)]
class SyncDashboard implements ShouldQueue { ... }

This differs from ShouldBeUnique — with debounce, the last dispatch wins (at execution time), while ShouldBeUnique rejects additional dispatches at dispatch time.

Pull Request: #59507 by @matthewnessworthy

JSON Responses for Health Route

The framework's built-in health route (/up) now returns JSON when the request expects JSON. Previously, the route always rendered HTML, which was awkward for API-only applications whose load balancers and uptime monitors expect JSON.

When $request->expectsJson() is true, /up now returns:

{"status": "Application is up"}

Or when a DiagnosingHealth listener throws and debug mode is off:

{"status": "Application experiencing problems"}

Status codes (200 / 500) remain unchanged, and non-JSON requests continue receiving the existing Blade page.

Pull Request: #59710 by @WendellAdriel

JsonFormatter

A new JsonFormatter provides structured JSON logging output, useful for log aggregation systems like the ELK stack or Datadog:

use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Illuminate\Support\Facades\Log;
 
$handler = new StreamHandler('php://stdout');
$handler->setFormatter(new \Illuminate\Log\Formatters\JsonFormatter);
 
Log::build(['handler' => $handler])->info('User logged in', ['user_id' => 1]);

Pull Request: #59756 by @cosmastech

Cloudflare Email Service Support

Laravel now supports Cloudflare Email Service for sending emails through Cloudflare's infrastructure. Configure the transport in config/services.php:

'cloudflare' => [
'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
'token' => env('CLOUDFLARE_TOKEN'),
],

Pull Request: #59735 by @dwightwatson

Array of Pivot Arrays for hasAttached

The factory hasAttached method now accepts an array of pivot arrays, making it easier to create models with different pivot values:

$user = User::factory()
->hasAttached(Role::factory()->count(2), [
['admin' => 'Y'],
['admin' => 'N'],
])
->create();

Pull Request: #59723 by @jackbayliss

Improved Model Assertions

Database model assertions in tests now accept arrays, allowing you to check multiple records in a single call:

$this->assertDatabaseHas(User::class, [
['name' => 'Alice', 'email' => 'alice@example.com'],
['name' => 'Bob', 'email' => 'bob@example.com'],
]);
 
$this->assertDatabaseMissing(User::class, [
['name' => 'Charlie', 'email' => 'charlie@example.com'],
]);

Pull Request: #59752 by @jackbayliss

Other Fixes and Improvements

Queue:

Validation:

  • Fix digits_between validation rule on non-string values (#59717 by @sumaiazaman)
  • Cast to string before preg_match in decimal, max_digits, and min_digits rules (#59739 by @sumaiazaman)

Enum Support:

Other:

  • Return null from Cursor::fromEncoded for malformed payloads (#59699 by @bipinks)
  • Validate MAC across all decryption keys (#59742 by @ma32kc)
  • Use generic TModel in additional places in Factory class (#59780 by @jnoordsij)

References

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
Validate and Convert HEIC Images in Laravel image

Validate and Convert HEIC Images in Laravel

Read article
Saga Lara Flow: Durable Workflows and Compensating Transactions on Laravel Queues image

Saga Lara Flow: Durable Workflows and Compensating Transactions on Laravel Queues

Read article
Reject Unexpected Array Keys with Laravel Validation image

Reject Unexpected Array Keys with Laravel Validation

Read article
Major performance improvements & security patches for Filament v4.12 and v5.7! image

Major performance improvements & security patches for Filament v4.12 and v5.7!

Read article
Laravel Boost Project Rules: Teach Agents Your Conventions image

Laravel Boost Project Rules: Teach Agents Your Conventions

Read article
Extract an Image's Dominant Color in Laravel image

Extract an Image's Dominant Color in Laravel

Read article