Polyscope - The agent-first dev environment for Laravel

Scheduler Attributes and Listener Discovery Control in Laravel 13.12.0

Last updated on by

Scheduler Attributes and Listener Discovery Control in Laravel 13.12.0 image

Laravel 13.12.0 adds the ability to attach metadata to scheduled events via a new withAttributes() method, a ShouldBeDiscovered interface that gives auto-discovered listeners control over their own registration, opt-out behavior for worker restarts on lost connections, SQLite file: URI support, and a handful of testing and string improvements.

  • Custom attributes on scheduled events via withAttributes()
  • ShouldBeDiscovered interface for conditional listener auto-discovery
  • Opt out of queue worker restart on lost connection
  • SQLite URI-based connections using the file: prefix
  • normalize parameter for Str::studly() and Str::pascal()
  • Client\Request::uri() for HTTP client middleware
  • assertJsonPathsCanonicalizing() on TestResponse
  • Prohibitable cache:clear and key:generate commands
  • Fix for scheduler lifecycle callback parameter resolution by type

What's New

Custom Attributes on Scheduled Events

Scheduled events now support a withAttributes() method that lets you attach arbitrary key-value data directly to an event. These attributes are accessible inside lifecycle callbacks — useful for tagging events with metadata for monitoring, metrics, or logging without resorting to parsing the command string or description.

Schedule::after(function (Event $event) {
$tag = $event->attributes['tag'] ?? null;
if ($tag) {
Metrics::increment(
$event->exitCode === 0 ? 'scheduled.succeeded' : 'scheduled.failed',
tags: ['command' => $tag]
);
}
})->group(function (Schedule $schedule) {
$schedule->command('audio:import-podcasts --only-premium')
->withoutOverlapping()
->withAttributes(['tag' => 'import-premium-podcasts']);
 
$schedule->command('audio:import-free')
->withoutOverlapping()
->withAttributes(['tag' => 'import-free-podcasts']);
});

PR: #60255 by @cosmastech

Listener Discovery Opt-Out via ShouldBeDiscovered

A new ShouldBeDiscovered interface lets auto-discovered listeners decide whether to register themselves. Implementing the interface and returning false from its static shouldBeDiscovered() method prevents the listener from being included during discovery, without needing to remove it from the listeners directory or move logic into the handle method.

This is particularly useful for listeners that implement ShouldQueue — skipping discovery at the class level means the job is never dispatched at all, rather than being queued and then deciding to do nothing in handle.

use Illuminate\Contracts\Events\ShouldBeDiscovered;
use Illuminate\Contracts\Queue\ShouldQueue;
 
class NotifyExternalCrm implements ShouldBeDiscovered, ShouldQueue
{
public static function shouldBeDiscovered(): bool
{
return app()->environment('production');
}
 
public function handle(CustomerRegistered $event): void
{
Http::post(config('services.crm.webhook'), [
'email' => $event->customer->email,
]);
}
}

PR: #60209 by @jackbayliss

Opt Out of Worker Restart on Lost Connection

Queue workers currently restart themselves whenever a database connection is lost. This can cause boot-looping when using multiple database connections (such as replicas) and one becomes temporarily unavailable. A new static property lets you disable this behavior:

use Illuminate\Queue\Worker;
 
Worker::$stopOnLostConnection = false;

When set to false, the worker logs the lost connection and continues polling rather than exiting. This is opt-in and does not change the default behavior.

PR: #60201 by @jackbayliss

SQLite URI-Based Connections

SQLite connections now accept the file: URI prefix format introduced in PHP 8.1 and PDO. This allows specifying SQLite options such as cache=shared and mode=ro directly in the connection string:

'default' => [
'driver' => 'sqlite',
'database' => 'file:/absolute/path/to/database.sqlite?cache=shared',
],

PR: #60261 by @crynobone

Str::studly() and Str::pascal() Normalization

Both Str::studly() and Str::pascal() now accept a normalize parameter. When true, any all-uppercase word segment is lowercased before conversion, so strings like ALL_CAPS or acronyms produce the expected StudlyCase output:

Str::studly('ALL_CAPS'); // → 'ALLCAPS'
Str::studly('ALL_CAPS', normalize: true); // → 'AllCaps'
 
Str::studly('CBOR', normalize: true); // → 'Cbor'
Str::studly('AllJersey', normalize: true); // → 'AllJersey' (mixed-case unchanged)

The default is false, so existing behavior is preserved.

PR: #60229 by @hotmeteor

Client\Request::uri() for HTTP Client Middleware

HTTP client request objects now have a uri() method that returns the full UriInterface for the request. This is useful in middleware when you need the complete URI — including scheme, host, path, and query string — without manually reconstructing it:

Http::withRequestMiddleware(function (RequestInterface $request) {
$uri = $request->uri(); // returns a UriInterface
Log::info('Outgoing request', ['url' => (string) $uri]);
return $request;
})->get('https://example.com/api/resource');

PR: #60282 by @stevebauman

assertJsonPathsCanonicalizing() on TestResponse

A new assertJsonPathsCanonicalizing() method on TestResponse asserts that a set of JSON paths contain the expected values, comparing them in a canonicalized (order-independent) way. This complements the existing assertJsonPath() and assertJsonPathCanonicalizing() for single paths.

PR: #60225 by @Tresor-Kasenda

Prohibitable cache:clear and key:generate Commands

Both cache:clear and key:generate Artisan commands can now be prohibited using Artisan::prohibit(). This is consistent with other prohibitable commands and useful in production environments where you want to prevent accidental key rotation or cache flushes:

// In AppServiceProvider::boot()
if ($this->app->isProduction()) {
\Illuminate\Console\Commands\KeyGenerateCommand::prohibit();
}

PRs: #60215, #60224 by @jackbayliss

Scheduler Callback Parameter Resolution Fixed

A bug introduced in v13.10.0 caused scheduled event lifecycle callbacks to only inject the Event instance when the parameter was literally named $event. Parameters with any other name — such as $scheduledEvent or $task — would cause a BindingResolutionException. The fix now resolves the parameter by type rather than by name, matching how the rest of the Laravel container works:

// Previously failed with BindingResolutionException
Schedule::command('inspire')
->before(function (Event $scheduledEvent) {
Log::info("Starting {$scheduledEvent->command}");
});

PR: #60197 by @kayw-geek

Miscellaneous Fixes and Improvements

  • Fix path separator encoding in LocalFilesystemAdapter and temporaryUrl — path separators are no longer double-encoded for local disk operations (#60194, #60230 by @jackbayliss, @kayw-geek)
  • Fix async HTTP retries with array backoff values — passing an array of backoff intervals to async HTTP retry now works correctly (#60214 by @LucasCavalheri)
  • JsonSchema fluent boolean flags can now be unset — calling a fluent boolean setter with false now removes the flag rather than setting it to false in the schema output (#60239 by @LucasCavalheri)
  • Factory pivot stub — the generated stub for pivot models now includes a factory reference (#60204 by @ludo237)
  • Up/Down commands report exceptionsdb:wipe/inspire and similar commands now surface exceptions through the registered exception handler (#60232 by @jackbayliss)
  • ManagedQueueNotFoundException for missing managed queues — a dedicated exception is thrown when a managed queue cannot be found, improving error messages for Laravel Cloud queue setups (#60275 by @kieranbrown)

References

Paul Redmond photo

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

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Visit Laravel Cloud
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $9500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Harpoon: Next generation time tracking and invoicing logo

Harpoon: Next generation time tracking and invoicing

The next generation time-tracking and billing software that helps your agency plan and forecast a profitable future.

Harpoon: Next generation time tracking and invoicing
Laravel Cloud logo

Laravel Cloud

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Cloud
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
SerpApi logo

SerpApi

Access real-time search engine results through a simple API—no more scraping headaches! Use it for AI applications, SEO tools, product research, travel information, and more

SerpApi
PhpStorm logo

PhpStorm

The go-to PHP IDE with extensive out-of-the-box support for Laravel and its ecosystem.

PhpStorm
Acquaint Softtech logo

Acquaint Softtech

Acquaint Softtech offers AI-ready Laravel developers who onboard in 48 hours at $3000/Month with no lengthy sales process and a 100 percent money-back guarantee.

Acquaint Softtech
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit

The latest

View all →
The PHP Foundation Launches an Ecosystem Security Team image

The PHP Foundation Launches an Ecosystem Security Team

Read article
Manage Subscription Plans and Entitlements in Laravel with Laravel Entitlements image

Manage Subscription Plans and Entitlements in Laravel with Laravel Entitlements

Read article
Laravel Fluent Validation: An Object-Oriented Rule Builder image

Laravel Fluent Validation: An Object-Oriented Rule Builder

Read article
Moat: A Security Review for Your GitHub Account image

Moat: A Security Review for Your GitHub Account

Read article
Frontend Nation 2026 Returns June 3-4 with Laravel in the Lineup image

Frontend Nation 2026 Returns June 3-4 with Laravel in the Lineup

Read article
Laravel Paper: A Flat-File Eloquent Driver image

Laravel Paper: A Flat-File Eloquent Driver

Read article