Laravel 13.25 додає глобальний перемикач паузи, який зупиняє кожну чергу на кожному з'єднанні однією командою, замінює процес-ранер для artisan dev на табований термінальний UI та дозволяє повертати екземпляр Image безпосередньо з роута. Команда Laravel випустила версію 13.25.0 11 серпня 2026 року.
Глобальна пауза для всіх черг
Laravel вже давно вміє призупиняти окремі черги, але така гранулярність створювала проблеми під час деплою. Додаток із десятком воркерів, розподілених між кількома іменованими чергами, вимагав паузи кожної пари з'єднання та черги за іменем, а ці імена змінюються в міру додавання й видалення функцій. Використання режиму обслуговування натомість припиняло роботу всього сайту, коли вам потрібно було лише зупинити резервування завдань воркерами.
Обидві консольні команди тепер приймають прапорець --all:
php artisan queue:pause --all
php artisan queue:resume --all
Аргумент черги став опціональним, щоб звільнити місце для нього, і той самий перемикач доступний у фасаді:
use Illuminate\Support\Facades\Queue;
Queue::pauseAll();
Queue::resumeAll();
Глобальний перемикач - це один ключ кешу, який воркери перевіряють поряд із ключами окремих черг, тому isPaused() та getPausedQueues() повідомляють про паузу черги, коли ввімкнено будь-який із перемикачів. Вони незалежні: resumeAll() очищає глобальний прапорець і залишає призупиненим все, що ви призупинили індивідуально, що є навмисним, щоб скрипт деплою не міг випадково перезапустити чергу, яку хтось зупинив навмисне.
Дві нові події, QueuesPaused та QueuesResumed, спрацьовують поряд із існуючими подіями для окремих черг QueuePaused та QueueResumed.
Новий термінальний UI для artisan dev
Команда artisan dev раніше використовувала concurrently, який об'єднує всі процеси в одну стрічку. Коли Vite-ребілд, queue worker та Pail пишуть одночасно, знайти потрібний рядок стає завданням із прокручуванням.
Тепер команда працює через @laravel/multiplex - термінальний UI із кожним процесом у власній вкладці, пошуком, перезапуском окремих процесів, очищенням логів та автоматичним перезапуском процесів, що падають. Доступні три режими: tabs (за замовчуванням), stream (один об'єднаний потік із прокручуванням і пошуком) та inline (звичайний вивід, що використовується автоматично без TTY). Вибирайте режим для кожного запуску:
php artisan dev --stream
php artisan dev --timestamps --no-restart
Або встановіть типовий режим для проєкту в сервіс-провайдері:
use Illuminate\Foundation\DevCommands;
DevCommands::stream();
DevCommands::withTimestamps();
DevCommands::disableAutoRestart();
DevCommands::bufferSize(5000);
Після виходу з команди буферизовані логи виводяться в основний термінал, щоб нічого не втратилося. Windows повертається до concurrently, оскільки multiplex наразі підтримує лише macOS і Linux, а мінімальна версія Node для нового шляху - v22.13.
Зображення як HTTP-відповіді
Вбудоване API для зображень могло трансформувати картинку та зберегти її, але повернення через HTTP вимагало виклику toBytes() та самостійного складання відповіді. Image тепер реалізує Responsable, тому роут може повертати екземпляр безпосередньо:
use Illuminate\Support\Facades\Image;
Route::get('/avatars/{user}', function (User $user) {
return Image::fromStorage($user->avatar_path)
->cover(200, 200)
->toWebp()
->quality(80);
});
toResponse() повертає 200 із обробленими байтами та Content-Type, зчитаним із результату, тож заголовок відповідає формату, створеному конвеєром, а не вихідному файлу.
Два менші доповнення в цій області. Image::fromStream() створює екземпляр із потокового ресурсу, читаючи його ліниво та викидаючи ImageException, якщо потік не дає нічого:
$image = Image::fromStream(Storage::disk('s3')->readStream($path));
А toFormat() тепер публічний, що замінює match-вираз, який ви інакше написали б для перетворення рядка формату від користувача на правильний виклик toWebp() або toAvif():
return Image::fromUpload($request->file('photo'))
->toFormat($request->string('format'))
->quality(80);
Непідтримуваний формат викидає ImageException замість того, щоб пройти далі.
Покращення спостережуваності черг
Завдання, яке не диспетчеризується через те, що утримується блокування ShouldBeUnique, зникає без сліду, що ускладнює розрізнення працюючого обмеження унікальності від блокування, яке ніколи не звільняється. Нова подія UniqueJobSkipped несе завдання, яке було пропущено:
use Illuminate\Queue\Events\UniqueJobSkipped;
Event::listen(function (UniqueJobSkipped $event) {
Log::info('Skipped unique job', ['job' => $event->job::class]);
});
JobTimedOut отримала третю властивість, $timeout, що містить кількість секунд, яку було перевищено. Воркер, запущений із queue:work --timeout=120, застосовує власний таймаут до кожного завдання, яке виконує, тож без значення в події не було способу відрізнити таймаут завдання від таймауту воркера.
Сповіщення тепер враховують fail-on-timeout. SendQueuedNotifications читає властивість $failOnTimeout або атрибут #[FailOnTimeout] із сповіщення, що важливо, коли таймаут залишає вас у невпевненості, чи третя сторона вже доставила повідомлення:
use Illuminate\Queue\Attributes\FailOnTimeout;
#[FailOnTimeout]
class OrderShipped extends Notification implements ShouldQueue
{
//
}
Нарешті, QueueFake тепер призначає uuid підробленим завданням, тож методи інспекції черги повертають ту саму форму, що й із реальним драйвером, і код додатка, що читає $job->uuid, стає тестованим.
Інші можливості
withoutCookies() для відповідей
Видалення кількох cookies означало ланцюжок withoutCookie() для кожного імені. Форма в множині приймає масив:
return response('OK')->withoutCookies(['session', 'tracking', 'preferences']);
Вона перебирає withoutCookie(), тож опціональні аргументи $path та $domain застосовуються до кожного cookie в масиві, і екземпляри cookie працюють так само, як і імена.
foreignUlidFor() у Schema
foreignIdFor() вже виявляє трейт HasUlids і створює ULID-колонку, але не було явного помічника, що відповідає foreignUuidFor(). Тепер тріо завершене:
$table->foreignUlidFor(User::class)->constrained();
Помічник виводить ім'я колонки, пов'язану таблицю та посиланий ключ із моделі, створюючи колонку char(26) та відповідне обмеження зовнішнього ключа.
Request::all() віддає перевагу Input над Files
Request::all() об'єднувало input bag і file bag через array_replace_recursive(), з файлами, застосованими останніми, тож поле файлу та поле вводу з однаковим іменем перетворювалися на UploadedFile. Тепер порядок змінено, і перемагає input:
// POST із input email=taylor@laravel.com та файлом також названим email
$request->all(); // ['email' => 'taylor@laravel.com']
$request->email; // 'taylor@laravel.com'
$request->file('email') // все ще UploadedFile
Вкладені ключі об'єднуються так само, тож файл profile.avatar і input profile.name все ще обидва з'являються, а input має перевагу лише там, де ключі фактично конфліктують.
Виправлення та покращення
Http::globalOptions() та глобальні middleware застосовувалися до власного long poll unix socket агента хмари фреймворку, де опція на кшталт force_ip_resolve => v4 порушує з'єднання сокета. Новий Factory::withoutGlobalConfiguration() closure ізолює трафік агента від конфігурації клієнта на рівні додатка
- Події broadcast у черзі втрачали значення
true за замовчуванням для $deleteWhenMissingModels, тож модель, видалена до того, як воркер підхопив завдання, падала з ModelNotFoundException замість відкидання
Gate::forUser() копіював здібності, політики та callbacks, але не налаштовану відповідь про відмову за замовчуванням, тож gate, налаштований із Response::denyAsNotFound(), повертався до типового значення фреймворку
Str::substrReplace() викидав TypeError для аргументів-масивів після того, як багатобайтове переписування передало їх прямо до mb_substr(). Виклики з масивами тепер делегуються нативному PHP substr_replace()
- Виправлено повагу до імен черг backed enum під час постановки в чергу mailables, драйвери черг тепер відповідають fake для імен черг enum,
MailFake зберігає розв'язання черги для mailables у черзі, а масові push до DatabaseQueue поважають dispatch після commit
- Деактивація режиму обслуговування з підтримкою кешу між двома пошуками кешу middleware викидала
TypeError; middleware тепер перевіряє стан так само, як це вже робилося для файлового драйвера
- Виправлено типізації для
Arr::prependKeysWith(), Str::numbers(), getMigrationBatches(), getRememberToken(), регістратора прив'язки роутів та ColumnDefinition::unsigned()
- Додано підтримку
brick/math ^0.19