Команда Laravel випустила версію 13.26.0 18 серпня 2026 року. Реліз додає драйвер файлової системи read-through, який шарує основний диск поверх резервного та просуває файли при першому читанні, можливість відкладання (debouncing) для слухачів подій у черзі, метод Queue::forward() для перенаправлення черг без змін у класах завдань, а також низку покращень роботи з процесами, Eloquent та Redis.
Файлові системи з автопросуванням (Read-Through)
Новий драйвер read-through об'єднує два існуючі диски в один. При читанні спочатку перевіряється основний диск, а при його відсутності відбувається звернення до резервного з автоматичним копіюванням файлу на основний диск. Таким чином основний диск поступово заповнюється лише тими файлами, які реально запитуються. Операції запису, видалення та отримання списку директорій виконуються лише на основному диску:
'assets' => [
'driver' => 'read-through',
'primary' => 'r2',
'fallback' => 'legacy-s3',
],
Диски можна вказувати як назви (як у прикладі вище) або визначати безпосередньо як масиви конфігурації. Опція copy => false дозволяє віддавати файли з резервного диску без просування на основний, що підходить для середовищ розробки, які працюють з продакшн-файлами. Помилки просування за замовчуванням ігноруються, щоб операція читання ніколи не провалювалася через невдале копіювання. Встановіть throw_on_promotion_failure => true, щоб викидати виключення при помилках.
Функціональність додана @taylorotwell у /#61140, опція без копіювання - @jimbojsb у /#61155.
Відкладені слухачі подій у черзі
Атрибут #[DebounceFor], який додав можливість відкладання для завдань у черзі в Laravel 13.6, тепер працює і зі слухачами подій у черзі. Коли одна й та сама подія спрацьовує багаторазово, виконується лише останнє відправлення в межах вікна:
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\DebounceFor;
#[DebounceFor(30, maxWait: 120)]
class UpdateProductSearchIndex implements ShouldQueue
{
public function debounceId(ProductUpdated $event): string
{
return (string) $event->product->getKey();
}
public function handle(ProductUpdated $event): void
{
// переіндексація виконається один раз із найновішим станом
}
}
Метод debounceId() обмежує вікно для кожного окремого ресурсу, параметр maxWait встановлює максимальний час, протягом якого активний потік подій може відкладати виконання роботи. Відкладання застосовується лише до цього слухача, тому інші слухачі тієї ж події виконуються кожного разу. Відкладений слухач не може одночасно реалізовувати ShouldBeUnique - диспетчер викине LogicException, оскільки унікальність зберігає перше відправлення, а відкладання - останнє.
Додано @stevebauman у /#61169.
Метод Queue::forward()
Метод Queue::forward() перенаправляє все, що відправляється до певної черги, на іншу чергу, інше з'єднання або обидва варіанти одночасно. Це робиться в одному місці в сервіс-провайдері без редагування класів завдань або місць їхнього відправлення:
use Illuminate\Support\Facades\Queue;
Queue::forward('reports', 'reports.fifo', 'cloud'); // перейменування і зміна з'єднання
Queue::forward('payments', connection: 'cloud'); // збереження назви, зміна з'єднання
Queue::forward('updates', 'notifications'); // перейменування на тому ж з'єднанні
Queue::forward([
'reports' => 'reports.fifo',
'emails' => 'emails.fifo',
], connection: 'cloud');
Перенаправлення застосовуються через той самий механізм, що й Queue::route(), тому вони працюють незалежно від способу потрапляння завдання до черги. Додано @jackbayliss у /#61188.
Покращення роботи з процесами
Три нові можливості фасаду Process, усі від @xurshudyan. Пули процесів та їхні результати тепер ітеровані (/#61184). Раніше foreach над ProcessPoolResults ітерував публічні властивості, яких немає, тому цикл для перевірки кодів виходу мовчки нічого не перевіряв. Обидва класи тепер реалізують IteratorAggregate.
Тайм-аут простою тепер викидає власний виняток ProcessIdleTimedOutException (/#61182). Два тайм-аути означають протилежні речі - завислий процес проти повільного, але обидва раніше викидали ProcessTimedOutException:
try {
Process::timeout(600)->idleTimeout(30)->run('./deploy.sh');
} catch (ProcessIdleTimedOutException $e) {
// немає виводу протягом 30 секунд: вбити і сповістити
} catch (ProcessTimedOutException $e) {
// досі виробляє вивід, просто повільний: повторити з більшим часом
}
Фейк Process отримав допоміжні методи для перевірок (/#61193, /#61197): assertRanCount(2) перевіряє кількість запущених процесів, assertRanInOrder([...]) перевіряє послідовність, а recorded() фільтрує записані пари процесів та результатів за допомогою callback. Перевірки порядку та кількості приймають як рядкові команди, так і масиви.
Доповнення Eloquent Builder
Методи orWhereKey() та orWhereKeyNot() доповнюють існуючу пару whereKey(), замінюючи fallback orWhereIn('id', ...) у запитах, де потрібна умова за первинним ключем у гілці or (@calebdw, /#61154).
Методи wherePivot() та orWherePivot() приймають closure, яке отримує builder, обмежений до моделі pivot, тому scope, визначені на користувацькому pivot-класі, можна викликати з запиту зв'язку (@calebdw, /#61150):
$project->subscribers()
->wherePivot(fn ($query) => $query->active()->notMuted())
->get();
Метод inOrderOf() тепер приймає enum у списку значень, відповідно до підтримки enum в інших місцях query builder (@ziadoz, /#61147):
$orders = Order::query()
->inOrderOf('status', [Status::Pending, Status::Processing, Status::Shipped])
->get();
Зв'язки також отримали аксесор getRelatedClass() (/#61222).
Покращена видимість Queue Worker
Нова подія JobReleased спрацьовує, коли завдання повертається до черги зі свого власного обробника або з middleware, такого як WithoutOverlapping (/#61108). Існуюча подія JobReleasedAfterException покривала лише випадки повернення через виняток, тому повернення через перекриття було невидимим.
Worker тепер також виводять повідомлення, коли черга, яку вони обробляють, призупинена, і знову, коли вона відновлюється (/#61142). З моменту появи API призупинення призупинений worker просто переставав виводити інформацію, що виглядало як зависання. Worker, запущений проти вже призупиненої черги, також повідомляє про це. Обидва внески від @jackbayliss.
Інші виправлення та покращення
- Тепер підтримується Guzzle 8 поряд із Guzzle 7 (/#60321)
- Команда
db:seed --class=SpecificSeeder показує той самий вивід прогресу RUNNING/DONE, який вкладені seeders вже отримували через Seeder::call() (/#61186)
- Серія покращень Redis: перевірки призупинених черг більше не виконують читання між слотами на кластерах (/#61139), очищення тегів кешу сканує кожен головний вузол (/#61174) і більше не зациклюється на застарілих тегах (/#61181), невдалий pipeline або транзакція залишає з'єднання придатним (/#61183), клієнт phpredis перебудовується після помилки відповіді кластера (/#61214), деякі команди повторюються при тимчасових збоях (/#61175)
- Драйвер Cloud queue відкриває
managedQueues() (/#61149), події керованих черг просувають вкладені дані на верхній рівень (/#61209), тайм-аути long-poll cloud agent повторюються (/#61177)
- Метод
throwUnless() більше не мовчить у випадках, коли має викидати виняток (/#61217), порядок ключів вводу Request зберігається при об'єднанні файлів (/#61221)
- Налаштування
readonly=false дотримується в конфігурації Microsoft SQL Server (/#61141), одинарні лапки екрануються в атрибутах JSON-шляху Postgres (/#61192), параметри запиту JSON:API у вигляді масивів більше не викликають TypeError (/#61206)
- Команда
pail для розробки реєструється лише коли встановлено Pail (/#61157), artisan dev припиняє постійно пропонувати встановити @laravel/multiplex (/#61167), закріплена версія multiplex видалена (/#61171)
- Покращення для тестування: помилки сесії зберігають оригінальне повідомлення про провал твердження (/#61201), виправлено проблему повторного використання
setTestNow() з now() (/#61190), docblock expectsQuestion() дозволяє масиви відповідей (/#61223)
Примітки щодо оновлення
Для типових застосунків не очікується breaking changes. Два моменти, про які варто знати: слухач із атрибутом #[DebounceFor] тепер викидає LogicException, якщо він також реалізує ShouldBeUnique, а внутрішня логіка whereKey() / whereKeyNot() була консолідована у /#61154, що має значення лише якщо ви розширюєте Eloquent builder і перевизначаєте ці методи. Перегляньте changelog для деталей по кожному PR при оновленні.