Laravel 13.12.0 отримав можливість додавати метадані до запланованих подій через метод withAttributes(), інтерфейс ShouldBeDiscovered для гнучкого керування реєстрацією слухачів, опцію скасування перезапуску воркерів при втраті з’єднання, підтримку SQLite file: URI, а також низку покращень для рядків та тестування.
- Довільні атрибути для запланованих подій через
withAttributes() - Інтерфейс
ShouldBeDiscoveredдля умовної автореєстрації слухачів - Можливість вимкнути перезапуск воркерів черги при втраті з’єднання
- Підключення до SQLite через URI з префіксом
file: - Параметр
normalizeдля методівStr::studly()таStr::pascal() - Метод
Client\Request::uri()для middleware HTTP-клієнта - Метод
assertJsonPathsCanonicalizing()уTestResponse - Можливість заборонити команди
cache:clearтаkey:generate - Виправлення резолвінгу параметрів за типом у колбеках планувальника
# Що нового
# Кастомні атрибути запланованих подій
Заплановані події тепер підтримують метод withAttributes(), який дозволяє додавати довільні пари ключ-значення безпосередньо до події. Ці атрибути доступні всередині колбеків життєвого циклу — це зручно для маркування подій метаданими для моніторингу, збору метрик або логування без необхідності парсити рядок команди чи її опис.
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 від @cosmastech
# Умовна реєстрація слухачів через ShouldBeDiscovered
Новий інтерфейс ShouldBeDiscovered дозволяє автовиявленим слухачам (listeners) самостійно вирішувати, чи потрібно їх реєструвати. Реалізація інтерфейсу та повернення false зі статичного мето shouldBeDiscovered() запобігає реєстрації слухача без необхідності видаляти файл або додавати перевірки в метод handle.
Це особливо корисно для слухачів, що використовують ShouldQueue: скасування реєстрації на рівні класу означає, що задача взагалі не потрапить у чергу, замість того, щоб бути поставленою і нічого не робити вже під час виконання.
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 від @jackbayliss
# Скасування перезапуску воркерів при втраті з'єднання
За замовчуванням воркери черги перезапускаються при втраті зв'язку з базою даних. Це може призвести до циклічних помилок (boot-looping), якщо ви використовуєте кілька з'єднань (наприклад, репліки) і одне з них тимчасово недоступне. Нова статична властивість дозволяє вимкнути цю поведінку:
use Illuminate\Queue\Worker;
Worker::$stopOnLostConnection = false;
Якщо встановлено false, воркер просто зафіксує втрату з'єднання в логах і продовжить роботу замість завершення процесу.
PR: #60201 від @jackbayliss
# Підтримка SQLite URI
З'єднання SQLite тепер підтримують формат префікса file:, представлений у PHP 8.1 та PDO. Це дозволяє вказувати додаткові параметри SQLite, як-от cache=shared або mode=ro, безпосередньо в рядку підключення:
'default' => [
'driver' => 'sqlite',
'database' => 'file:/absolute/path/to/database.sqlite?cache=shared',
],
PR: #60261 від @crynobone
# Нормалізація в Str::studly() та Str::pascal()
Методи Str::studly() та Str::pascal() отримали параметр normalize. Якщо він має значення true, слова, написані повністю великими літерами, будуть переведені в нижній регістр перед конвертацією. Це дозволяє коректно обробляти абревіатури або рядки на кшталт ALL_CAPS:
Str::studly('ALL_CAPS'); // → 'ALLCAPS'
Str::studly('ALL_CAPS', normalize: true); // → 'AllCaps'
Str::studly('CBOR', normalize: true); // → 'Cbor'
Str::studly('AllJersey', normalize: true); // → 'AllJersey' (змішаний регістр не змінюється)
Типове значення — false, тому існуюча поведінка збережена.
PR: #60229 від @hotmeteor
# Client\Request::uri() для middleware HTTP-клієнта
Об'єкти запитів HTTP-клієнта тепер мають метод uri(), який повертає повний UriInterface. Це корисно в middleware, коли потрібен повний URI (включаючи протокол, хост, шлях та параметри запиту) без ручного збирання рядка:
Http::withRequestMiddleware(function (RequestInterface $request) {
$uri = $request->uri(); // повертає UriInterface
Log::info('Outgoing request', ['url' => (string) $uri]);
return $request;
})->get('https://example.com/api/resource');
PR: #60282 від @stevebauman
# assertJsonPathsCanonicalizing() у TestResponse
Новий метод assertJsonPathsCanonicalizing() у TestResponse дозволяє перевірити, чи містять вказані JSON-шляхи очікувані значення, ігноруючи порядок елементів. Це доповнює існуючі методи перевірки окремих шляхів.
PR: #60225 від @Tresor-Kasenda
# Можливість заборони команд cache:clear та key:generate
Команди Artisan cache:clear та key:generate тепер можна заблокувати за допомогою Artisan::prohibit(). Це допомагає запобігти випадковому скиданню кешу або зміні ключів шифрування у продуктивному середовищі:
// В AppServiceProvider::boot()
if ($this->app->isProduction()) {
\Illuminate\Console\Commands\KeyGenerateCommand::prohibit();
}
PRs: #60215, #60224 від @jackbayliss
# Виправлення резолвінгу параметрів у планувальнику
У версії v13.10.0 виникла помилка, через яку колбеки запланованих подій ін'єктували екземпляр Event тільки якщо параметр мав назву $event. Будь-яка інша назва призводила до BindingResolutionException. Тепер параметри розпізнаються за типом, що відповідає стандартній логіці контейнера Laravel:
// Раніше викликало BindingResolutionException
Schedule::command('inspire')
->before(function (Event $scheduledEvent) {
Log::info("Starting {$scheduledEvent->command}");
});
PR: #60197 від @kayw-geek
# Інші виправлення та покращення
- Виправлено кодування роздільників шляхів у
LocalFilesystemAdapterтаtemporaryUrl— тепер роздільники не кодуються двічі при операціях на локальному диску (#60194). - Асинхронні повтори HTTP-запитів тепер коректно працюють з масивом значень затримки (backoff) (#60214).
- Прапорці JsonSchema тепер можна скасовувати: виклик сетера з
falseвидаляє прапорець зі схеми, а не встановлює його якfalse(#60239). - Шаблон фабрики для півот-моделей тепер включає посилання на фабрику за замовчуванням (#60204).
- Команди db:wipe та inspire тепер передають винятки до зареєстрованого обробника помилок (#60232).
- Новий виняток ManagedQueueNotFoundException для Laravel Cloud покращує інформативність повідомлень про помилки у разі відсутності керованої черги (#60275).
Посилання