消息

Pause All Queues and a New artisan dev UI in Laravel 13.25

发布 更新
Pause All Queues and a New artisan dev UI in Laravel 13.25 image

Release updates

Never miss a Laravel release

Get an email whenever a new Laravel release lands.

Laravel 13.25 adds a global pause switch that stops every queue on every connection with one command, replaces the process runner behind artisan dev with a tabbed terminal UI, and lets an Image instance be returned directly from a route. The Laravel team released v13.25.0 on August 11, 2026.

  • queue:pause --allqueue:resume --all , plus Queue::pauseAll()Queue::resumeAll()
  • artisan dev runs through @laravel/multiplex with tabs, stream, and inline modes
  • Image 实现 Responsable , gains Image::fromStream() , 和 toFormat() is now public
  • A UniqueJobSkipped event, the timeout value on JobTimedOut , and fail-on-timeout for notifications
  • withoutCookies() on responses and a foreignUlidFor() schema helper
  • Request::all() now prefers input over files when the two collide

什么是新的

Pause Every Queue on Every Connection

Laravel has been able to pause an individual queue for a while, but the granularity worked against you during a deployment. An application with a dozen workers spread over several named queues had to pause each connection and queue pair by name, and those names change as features come and go. Reaching for maintenance mode instead takes the whole site down when all you wanted was for workers to stop reserving jobs.

Both console commands now take an --all 旗帜:

php 工匠 队列:暂停 - 全部
php 工匠 queue:resume - 全部

The queue argument became optional to make room for it, and the same switch is available on the facade:

使用 Illuminate\Support\Facades\Queue ;
队列 :: pauseAll ();
队列 :: resumeAll ();

The global switch is a single cache key that workers check alongside the per-queue keys, so isPaused()getPausedQueues() report a queue as paused when either switch is on. The two are independent: resumeAll() clears the global flag and leaves anything you paused individually still paused, which is deliberate so a deploy script cannot accidentally restart a queue somebody parked on purpose.

Two new events, QueuesPausedQueuesResumed , fire alongside the existing per-queue QueuePausedQueueResumed . Contributed by @jackbayliss#61126

阅读更多: Pause All Laravel Queues During a Deploy

artisan dev Runs Through @laravel/multiplex

artisan dev 命令 shelled out to concurrently , which interleaves every process into one scrolling feed. A Vite rebuild and a queue worker and Pail all writing at once makes finding the line you care about a scrolling exercise.

It now runs through @laravel/multiplex , a terminal UI with each process in its own tab, search, per-process restart and log clearing, and automatic restart of a process that crashes. Three modes are available, tabs (默认), stream (one interleaved feed you can scroll and search), and inline (plain output, used automatically when there is no TTY). Pick one per run:

php 工匠 开发 --stream
php 工匠 开发 --timestamps --no-restart

Or set the default for the project in a service provider:

使用 Illuminate\Foundation\DevCommands ;
DevCommands :: 溪流 ();
DevCommands :: 带有时间戳 ();
DevCommands :: disableAutoRestart ();
DevCommands :: bufferSize 5000 (英文):

When the command exits, the buffered logs are printed to the main terminal so nothing is lost on the way out. Windows falls back to concurrently , since multiplex currently supports macOS and Linux only, and the Node floor for the new path is v22.13. The full list of modes, flags, and registration methods is in the artisan dev terminal UI . Contributed by @joetannenbaum#61100

Images as HTTP Responses

first-party image API could transform an image and store it, but handing one back over HTTP meant calling toBytes() and assembling the response yourself. Image now implements Responsable , so a route can return the instance:

使用 Illuminate\Support\Facades\Image ;
路线 :: 得到 '/avatars/{user}' , 功能 用户 $user) {
返回 图像 :: fromStorage ($用户) -> avatar_path)
-> cover 200 , 200
-> toWebp ()
-> 质量 80 (英文):
});

toResponse() returns a 200 with the processed bytes and a Content-Type read from the output, so the header matches whatever format the pipeline produced rather than the source file.

Two smaller additions in the same area. Image::fromStream() builds an instance from a stream resource, reading it lazily and throwing an ImageException if the stream yields nothing:

$图像 = 图像 :: 来自流 贮存 :: 磁盘 's3' -> readStream ($path));

toFormat() is now public, which replaces the match statement you would otherwise write to turn a user-supplied format string into the right toWebp() 或者 toAvif() 称呼:

返回 图像 :: fromUpload 请求 -> 文件 '照片' ))
-> toFormat 请求 -> 细绳 '格式' ))
-> 质量 80 (英文):

An unsupported format throws an ImageException rather than falling through. All three contributed by @calebdw#61111 , #61109 , 和 #61110

Queue Observability Additions

A job that is not dispatched because a ShouldBeUnique lock is held disappears without a trace, which makes it hard to tell a working uniqueness constraint from a lock that is never released. The new UniqueJobSkipped event carries the job that was dropped:

使用 Illuminate\Queue\Events\UniqueJobSkipped ;
事件 :: 功能 UniqueJobSkipped $event) {
日志 :: 信息 'Skipped unique job' ,[ '工作' => $事件 -> 工作 ::班级 ]);
});

It fires from PendingDispatch when the unique lock cannot be acquired, alongside the existing JobDebounced event ( #61039 )。

JobTimedOut gained a third property, $timeout , holding the number of seconds that was exceeded. A worker started with queue:work --timeout=120 applies its own timeout to every job it runs, so without the value on the event there was no way to tell a job's own timeout from the worker's ( #61060 )。

Notifications now honor fail-on-timeout. SendQueuedNotifications reads a $failOnTimeout property or a #[FailOnTimeout] attribute off the notification, which matters when a timeout leaves you unsure whether a third party already delivered the message ( #61072 ):

使用 Illuminate\Queue\Attributes\FailOnTimeout ;
#[ FailOnTimeout ]
班级 订单已发货 延伸 通知 实现 应该排队
{
//
}

Finally, QueueFake now assigns a uuid to faked jobs, so the queue inspection methods return the same shape they do against a real driver and application code that reads $job->uuid is testable ( #60966 )。

withoutCookies() on Responses

Expiring several cookies meant chaining withoutCookie() once per name. The plural form takes an array:

返回 回复 'OK' -> withoutCookies ([ '会议' , '追踪' , “偏好” ]);

It loops over withoutCookie() , so the optional $path$domain arguments apply to every cookie in the array, and cookie instances work as well as names. Contributed by @khurshudyan#61115

foreignUlidFor() Schema Helper

foreignIdFor() already detects the HasUlids trait and produces a ULID column, but there was no explicit helper to match foreignUuidFor() . The trio is now complete:

$表 -> foreignUlidFor 用户 ::班级 -> 受限制 ();

The helper infers the column name, the related table, and the referenced key from the model, producing a char(26) column and the matching foreign key constraint. Contributed by @talaridisTh#61036

Request::all() Prefers Input Over Files

Request::all() merged the input bag and the file bag with array_replace_recursive() , with files applied last, so a file field and an input field sharing a name resolved to the UploadedFile . The order is now reversed, and input wins:

// POST with input email=taylor@laravel.com and a file also named email
$请求 -> 全部 (); // ['email' => 'taylor@laravel.com']
$请求 -> 电子邮件; // 'taylor@laravel.com'
$请求 -> 文件 '电子邮件' // still the UploadedFile

Nested keys merge the same way, so a profile.avatar file and a profile.name input still both appear, with input taking precedence only where the keys actually collide. file() is unaffected. Contributed by @泰勒韦尔#61099

其他修复和改进

  • Http::globalOptions() and global middleware were applied to the framework's own cloud agent unix socket long poll, where an option like force_ip_resolve => v4 breaks the socket connection. A new Factory::withoutGlobalConfiguration() closure isolates agent traffic from application-level client configuration ( #61064 , #61068
  • Queued broadcast events lost the true default for $deleteWhenMissingModels , so a model deleted before the worker picked up the job failed with a ModelNotFoundException instead of being discarded ( #61074
  • Gate::forUser() copied abilities, policies, and callbacks but not the configured default denial response, so a gate set up with Response::denyAsNotFound() fell back to the framework default ( #61087
  • Str::substrReplace() threw a TypeError on array arguments after the multibyte rewrite passed them straight to mb_substr() . Array calls now delegate to PHP's native substr_replace()#61105
  • Backed enum queue names are respected when queueing mailables ( #61066 ), queue drivers match the fake for enum queue names ( #61116 ), MailFake preserves queue resolution on queued mailables ( #61114 ), and bulk pushes to DatabaseQueue respect after-commit dispatch ( #60996
  • Deactivating cache-backed maintenance mode between the middleware's two cache lookups threw a TypeError ; the middleware now rechecks the state the same way it already did for the file driver ( #61121
  • Container::call left entries on the build stack when a dependency threw, so later resolutions saw a stale stack ( #61041
  • Typed cache getters interpolated an enum key into the type mismatch message, producing an Error instead of the intended InvalidArgumentException#61056
  • HEIC files reported incorrect dimensions ( #61010 ), LazyCollection::flip() skips values that cannot be array keys ( #61081 ), 和 #[WithoutTimestamps] is checked when a model decides whether to touch ( #61073
  • Retry callbacks on asynchronous HTTP requests receive the HTTP method as a third argument, matching synchronous requests, which previously caused an ArgumentCountError#61106
  • Non-stream resources are rejected in HTTP fake response bodies ( #61047 ), the Cloud log driver sets a socket timeout ( #61065 , #61082 ), and signed URL support was adjusted for Vapor ( #61129
  • schedule:list converts timezones correctly for range, step, and wildcard cron expressions ( #60913 ), Factory::insert() handles a count of zero ( #60911 ), and a failure while logging a deprecation no longer escalates to a fatal error ( #60907
  • Type annotation fixes for Arr::prependKeysWith()#61034 ), Str::numbers()#61053 ), getMigrationBatches()#60973 ), getRememberToken()#61067 ), the route binding registrar ( #61124 ), 和 ColumnDefinition::unsigned()#61123
  • 支持 brick/math ^0.19 ( #61133

参考

保罗·雷德蒙德照片

Laravel News 特约撰稿人。全栈 Web 开发人员兼作家。

赞助

acquaintsoft logo
了解软科技

以每小时 20 美元的价格聘请具备人工智能专业知识的 Laravel 开发人员。48 小时内即可开始工作。

访问 Acquaint Softtech
Sidecar Brings Statamic's Control Panel to Your Existing Markdown Sites image

Sidecar Brings Statamic's Control Panel to Your Existing Markdown Sites

阅读文章
Collections chunkBy() and Storage Path Hardening in Laravel 13.30 image

Collections chunkBy() and Storage Path Hardening in Laravel 13.30

阅读文章
Forte: Parse and Rewrite Laravel Blade Templates image

Forte: Parse and Rewrite Laravel Blade Templates

阅读文章
Compoships: Eloquent Relationships on Multiple Columns image

Compoships: Eloquent Relationships on Multiple Columns

阅读文章
MKSine: A Filament CMS with Plugins, Themes, and Blocks image

MKSine: A Filament CMS with Plugins, Themes, and Blocks

阅读文章
Cancel In-Flight Form Submissions in Inertia.js v3.7 image

Cancel In-Flight Form Submissions in Inertia.js v3.7

阅读文章