---
title: "Визначення домінантного кольору та підтримка HEIC у Laravel 13.24"
url: https://laravelukraine.com/blog/viznacennia-dominantnogo-kolyoru-zobrazen-ta-pidtrimka-heic-u-laravel-1324
date: 2026-08-06
source: https://laravel-news.com/laravel-13-24-0?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+laravelnews
---

# Визначення домінантного кольору та підтримка HEIC у Laravel 13.24

Laravel 13.24 розширює можливості фреймворка з обробки зображень, додаючи визначення домінантного кольору та підтримку форматів HEIC і AVIF. Крім того, реліз включає новий метод `modelKeys()` для Eloquent query builder, правило валідації `array_keys` та критичне виправлення продуктивності валідації, яке раніше могло затримувати запит понад хвилину при роботі з великими масивами. Команда Laravel випустила версію 13.24.0 4 серпня 2026 року.

## Визначення домінантного кольору в Image API

Перша офіційна Image API тепер може визначати середній колір зображення. Метод `dominantColor()` зменшує зображення до одного пікселя та повертає його колір у форматі hex-рядка:

```php
use Illuminate\Support\Facades\Image;

$color = Image::fromPath(storage_path('app/photo.jpg'))->dominantColor(); 
// "#8a6f4c"
```

Це значення можна використовувати як фоновий колір-заповнювач під час завантаження фотографії або для тонування картки за її власним зображенням. Результат мемоїзується для кожного екземпляра зображення, і якщо у pipeline є незавершені трансформації, вони виконуються перед визначенням кольору, тому ви отримуєте колір зображення, яке збираєтеся зберегти, а не оригіналу.

Те саме визначення доступне як значення фону. Передача `dominant` до методів `contain()` або `rotate()` заповнює порожні простір letterbox або кути повороту власним домінантним кольором зображення замість заздалегідь обраного:

```php
Image::fromUpload($request->file('photo'))
    ->contain(1200, 800, background: 'dominant')
    ->store('photos');
```

## Підтримка AVIF та HEIC на вході, вивід у HEIC

Раніше Image API підтримувала вивід AVIF через `toAvif()`, але відхиляла AVIF-файли на вході. Формати HEIC та HEIF відхилялися в обох напрямках, навіть попри те, що Intervention Image постачається з кодером для них. Фотографії безпосередньо з iPhone мають формат HEIC, тому завантаження доводилося конвертувати ще до потрапляння у фреймворк.

Тепер AVIF, HEIC та HEIF приймаються як вхідні формати, доступні `toHeic()` та `optimize('heic')` для виводу, і всі три формати розпізнаються правилом валідації `image`:

```php
Image::fromPath(storage_path('app/photo.heic'))
    ->usingImagick()
    ->cover(1200, 800)
    ->toAvif()
    ->quality(80)
    ->store('photos');
```

Обидві зміни базуються на можливостях image pipeline. `optimize('heif')` та MIME-тип `image/heif` нормалізуються до HEIC, тому файли зберігаються з канонічним розширенням `.heic`. Метод `Image::extension()` також вивчив псевдоніми `image/x-avif`, `image/x-heic` та `image/heif`. Обробка HEIC потребує збірки Imagick з вкомпільованим HEIC-кодеком.

## Метод modelKeys() у Eloquent Query Builder

Eloquent-колекції давно мали метод `modelKeys()`, але отримання того самого масиву безпосередньо з запиту означало самостійне зазначення первинного ключа через `pluck('id')`. Тепер query builder також має цей метод:

```php
$ids = Post::query()->where('published', true)->modelKeys(); 
// [1, 2, 3]
```

Він витягує кваліфіковане ім'я ключа з моделі, тому кастомний `$primaryKey` та з'єднані запити розв'язуються коректно без жорсткого кодування імені стовпця.

## Правило валідації array_keys

`Rule::array(['sort', 'direction'])` вже відхиляє масиви з неочікуваними ключами, але повідомляє про помилку загальним повідомленням "The options field must be an array", яке не каже читачеві, яка саме з двох перевірок провалилася. Нове правило валідації `array_keys` відповідає лише на питання про ключі і повідомляє про це:

```php
$request->validate([
    'options' => Rule::arrayKeys(['sort', 'direction']),
]);

// або як рядок
$request->validate([
    'options' => 'array_keys:sort,direction',
]);
```

Для вхідних даних `['sort' => 'name', 'colour' => 'red']` повідомлення про помилку: "The options field must only contain the following keys: sort, direction." Ключі є дозволеними, а не обов'язковими, тому це обмежує те, що може з'явитися, а не вимагає присутності всього. `required_array_keys` як і раніше покриває другу половину.

Доступні два плейсхолдери для кастомних повідомлень: `:values` для прийнятих ключів та `:unexpected` для ключів, які фактично спричинили помилку (зазвичай це корисніше в API-відповідях):

```php
$request->validate(
    ['options' => Rule::arrayKeys(['sort', 'direction'])],
    ['options.array_keys' => 'The :attribute field may not contain :unexpected.'],
);
// The options field may not contain colour.
```

Оскільки правило звітується як `ArrayKeys` у `$validator->failed()`, воно компонується з `array`, коли ви хочете, щоб обидві перевірки звітувалися незалежно. `Rule::array()` та його повідомлення залишаються без змін.

## Виправлення уповільнення валідації з wildcards на великих масивах

Розгортання правил `foo.*.bar` було квадратичним відносно кількості розгорнутих атрибутів. `ValidationRuleParser::explodeWildcardRules()` накопичував результати, переприсвоюючи значення, що повертається з `mergeRules()`, який приймав накопичений набір за значенням, тому copy-on-write клонував весь набір правил один раз на кожен розгорнутий атрибут. Весь час витрачався в `Validator::make()`, ще до запуску будь-якого правила.

Числа з pull request при використанні 17 правил під одним wildcard:

- 1,000 елементів: 0.98s → 0.11s
- 4,000 елементів: 18.59s → 0.47s
- 8,000 елементів: 85.13s → 0.98s

При 8,000 елементах payload становить лише близько 1.1 МБ, тому `post_max_size` та ліміти веб-сервера ніколи не спрацьовували. Правило `max:500` на масиві теж не допомагало, оскільки розгортання відбувалося перед його виконанням.

Тіло злиття перенесено в приватний метод, який приймає акумулятор за посиланням. `mergeRules()` та `mergeRulesForAttribute()` зберігають свої сигнатури, але `explodeWildcardRules()` більше не маршрутизується через останній, тому override там більше не впливатиме на розгортання wildcards.

## Виправлення Arr::forget(), що видаляв неправильний елемент

`Arr::forget()` скидав своє внутрішнє посилання назад до масиву верхнього рівня лише після обробки точного збігу ключа верхнього рівня. Після обробки ключа з крапкою посилання залишалося вказувати на вкладений масив, тому наступний ключ у списку розв'язувався відносно цього вкладеного масиву:

```php
$array = ['users' => ['name' => 'Joe', 'id' => 1], 'id' => 99];
Arr::forget($array, ['users.name', 'id']);
// до: ['users' => [], 'id' => 99]
// після: ['users' => ['id' => 1]]
```

Видалявся неправильний елемент, а запитуваний зберігався, без жодної помилки. Переміщення скидання на початок циклу виправляє це для всього, що делегує до хелпера, включно з `Arr::except()`, `Collection::except()`, `data_forget()` та `Uri::withoutQuery()`.

## Інші виправлення та покращення

- `CompiledRouteCollection` перебудовував кожен об'єкт `Route` при кожному виклику `get()`, `getByAction()` та `getRoutesByMethod()`, що робило 404 дорогими на закешованих маршрутах. Індекси пошуку з raw-атрибутів маршрутів прискорюють 404 проти 2,000 закешованих маршрутів з 71ms до 2.5ms
- Аксесор для існуючого атрибута, який також був у `$appends`, отримував `null` замість збереженого значення, тому `$model->price` та `$model->toArray()['price']` розходилися
- `Str::replace()` та `Str::remove()` делегують до `str_ireplace()` для нечутливого до регістру пошуку, яка обробляє лише ASCII. Багатобайтові пошукові терміни тепер використовують патерн `/iu`; ASCII-терміни йдуть оригінальним шляхом
- Резолв атрибута відношення всередині closure `Relation::noConstraints()` пропускав `where`-умову на первинному ключі. Новий `Relation::withConstraints()` обгортає резолв атрибутів
- `PostgresGrammar::wrapJsonPathAttributes()` інтерполював JSON path атрибути в quoted SQL літерали без екранування одинарних лапок, на відміну від інших драйверів
- Два псевдоніми, що вказують один на одного, змушували `Container::getAlias()` рекурсувати до вичерпання пам'яті; тепер кидається `LogicException`
- `NumberPrompt` був єдиним типом промпту без fallback, тому `number()` кидав помилку на Windows, в неінтерактивних контекстах та в тестах замість деградації до Symfony question
- `ShouldBeUniqueUntilProcessing` jobs тепер відстежують власника cache lock та звільняють через `restoreLock()`, тому retry не може звільнити lock, отриманий новішим dispatch
- Резолв атрибута `Guarded` пропускався на `Pivot`, який перевизначає `$guarded` з `[]` і тому провалював перевірку дефолту `['*']`
- Затримки jobs поважаються при bulk push до `QueueFake` та `FailoverQueue`, а batch testing fakes використовують immutable timestamps
- `LengthAwarePaginator` захищається від ділення на нуль, коли `perPage` дорівнює 0, а `CursorPaginator` переіндексує items консистентно в обох напрямках
- Більшість Artisan-команд перейшли від стилю `$name` плюс `getArguments()`/`getOptions()` до одного рядка `$signature`
- Логіка валідації URL була скоригована, і вирішено кілька PHP 8.5 deprecation попереджень щодо null array offset

## Примітки до оновлення

Брейкінг-змін для типових застосунків не очікується. Дві зміни варто перевірити, якщо ви розширюєте framework internals: `explodeWildcardRules()` більше не викликає `mergeRulesForAttribute()`, тому override цього методу не вплине на розгортання wildcards, і `Container::getAlias()` тепер кидає `LogicException` на циклічних псевдонімах, які раніше вичерпували пам'ять. Підтримка HEIC залежить від вашої збірки Imagick з включеним HEIC-кодеком.

Два pull request, що злилися під час цього циклу, були відкочені перед релізом. Перегляньте changelog для деталей по кожному PR при оновленні.
