Ендпоінти, що приймають набір опцій, мають підступну особливість: вони схильні до «тихих» помилок. Якщо клієнт надішле ?filter[stat us]=draft з одруківкою, ваш код шукатиме $filters['status'], нічого не знайде і поверне невідфільтрований список. Користувач не отримає помилку, відповідь виглядатиме коректно, а баг випливе згодом у формі скарги: «фільтр чомусь працює через раз».
Laravel 13.24 додає правило валідації array_keys саме для таких випадків: воно гарантує, що масив містить лише дозволені ключі, і чітко вказує, де саме допущено помилку.
# Правило
Працює як об'єктний, так і рядковий синтаксис:
use Illuminate\Validation\Rule;
$request->validate([
'filter' => Rule::arrayKeys(['status', 'author', 'tag']),
]);
// Еквівалент
$request->validate([
'filter' => 'array_keys:status,author,tag',
]);
Якщо передати ['status' => 'draft', 'stat us' => 'draft'], валідація видасть помилку:
The filter field must only contain the following keys: status, author, tag.
Ключі є дозволеними, але не обов'язковими. Правило обмежує те, що може з'явитися в масиві, але не вимагає присутності кожного елемента. Якщо вам потрібні обидві перевірки, required_array_keys все ще актуальне, і ці правила можна комбінувати:
'coordinates' => [
'required_array_keys:lat,lng',
Rule::arrayKeys(['lat', 'lng']),
],
Така комбінація означає: «мають бути саме ці ключі — ні більше, ні менше».
# Чому не використовувати array:key_1,key_2?
Метод Rule::array() вже давно вміє приймати список ключів і відхиляти несподівані параметри. Різниця полягає у повідомленні про помилку. Правило array відповідає на два запитання: «чи це масив?» та «чи містить він лише ці ключі?», але на обидва реагує однаковим текстом:
| Правило | Повідомлення при ['status' => 'draft', 'colour' => 'red'] |
|---|---|
array:status,author |
The filter field must be an array. |
array_keys:status,author |
The filter field must only contain the following keys: status, author. |
Перше повідомлення відверто дезорієнтує, адже значення є масивом. Крім того, у ньому немає плейсхолдера для некоректних ключів, тому ви не зможете створити кастомне повідомлення з їхнім переліком.
У $validator->failed() ці правила відображаються окремо як Array та ArrayKeys, тож ви можете використовувати обидва одночасно, щоб розрізняти типи помилок:
'filter' => ['array', Rule::arrayKeys(['status', 'author', 'tag'])],
Оскільки Rule::array() та його логіка не змінилися, ваша існуюча кодова база працюватиме без сюрпризів.
# Визначення помилкових ключів
Правило пропонує два плейсхолдери. :values містить список дозволених ключів (використовується за замовчуванням). :unexpected містить саме ті ключі, які спричинили збій. Це набагато корисніше для API-відповідей:
$request->validate(
['filter' => Rule::arrayKeys(['status', 'author', 'tag'])],
['filter.array_keys' => 'The :attribute field may not contain :unexpected.'],
);
// The filter field may not contain colour, sort.
Коли клієнт бачить, де саме він помилився, «гра в вгадування» перетворюється на швидке виправлення в один рядок. Для публічних API таке кастомне повідомлення варте зусиль.
# Приклад використання в ендпоінті
Поєднаємо все в одному FormRequest для маршруту, який приймає фільтрацію, сортування та вкладені зв'язки:
namespace App\Http\Requests;
use App\Enums\PostStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class IndexPostRequest extends FormRequest
{
public function rules(): array
{
return [
'filter' => ['sometimes', 'array', Rule::arrayKeys(['status', 'author', 'tag'])],
'filter.status' => ['sometimes', Rule::enum(PostStatus::class)],
'filter.author' => ['sometimes', 'integer', 'exists:users,id'],
'filter.tag' => ['sometimes', 'string', 'max:50'],
'sort' => ['sometimes', 'string', Rule::in(['title', '-title', 'published_at', '-published_at'])],
];
}
public function messages(): array
{
return [
'filter.array_keys' => 'Unknown filter: :unexpected. Allowed filters are :values.',
];
}
}
Тепер контролер може спокійно працювати з фільтрами без зайвих перевірок, оскільки до нього потраплять лише валідні ключі:
public function index(IndexPostRequest $request)
{
$filters = $request->validated('filter', []);
return PostResource::collection(
Post::query()
->when($filters['status'] ?? null, fn ($q, $status) => $q->where('status', $status))
->when($filters['author'] ?? null, fn ($q, $author) => $q->where('user_id', $author))
->when($filters['tag'] ?? null, fn ($q, $tag) => $q->whereRelation('tags', 'slug', $tag))
->paginate()
);
}
Без цього правила запит ?filter[autor]=3 просто повернув би всі пости, імітуючи успішну роботу. З ним клієнт отримає 422 помилку з чітким вказанням на autor.
# Валідація JSON-колонок
Це правило також корисне при записі даних, наприклад, коли колонка з налаштуваннями (settings/preferences) починає накопичувати зайвий непотріб із фронтенду:
'preferences' => ['sometimes', 'array', Rule::arrayKeys(['theme', 'timezone', 'digest_frequency'])],
'preferences.theme' => ['sometimes', Rule::in(['light', 'dark', 'system'])],
'preferences.timezone' => ['sometimes', 'timezone'],
'preferences.digest_frequency' => ['sometimes', Rule::in(['daily', 'weekly', 'never'])],
Якщо поле на фронтенді буде перейменовано, система одразу видасть помилку під час деплою, замість того, щоб записувати в базу ключі, які ніхто ніколи не прочитає.
# Важливі деталі
Декілька нюансів, які не очевидні з повідомлення про помилку:
-
Значення, що не є масивом, провалюють перевірку. Якщо передати
'filter' => 'draft', ви отримаєте повідомлення «must only contain the following keys», що виглядає дивно для рядка. Використовуйтеarray_keysразом із правиломarray, щоб спершу коректно відловлювати невідповідність типу. -
Правило вимагає хоча б один ключ. Виклик
'array_keys'без параметрів викличеInvalidArgumentExceptionпід час виконання валідації. Для заборони будь-яких ключів використовуйте правилоprohibited. -
Ключі можуть бути будь-яким об'єктом
Arrayable. Можна передавати колекції або спискиEnum(валідуються їхні значення):Rule::arrayKeys(FilterKey::cases()); Rule::arrayKeys(collect(config('filters.allowed'))); -
Підтримка варіативної форми. Запис
Rule::arrayKeys('status', 'author')ідентичний передачі масиву, що зручно при написанні коду в один рядок.
Правило було додано розробником @nebarg у PR #60918.
# Що ще почитати
- Laravel Validation: практичний посібник із прикладами — про Form Requests, кастомні правила та обробку помилок.
- Підтримка домінантного кольору зображень та HEIC у Laravel 13.24 — повний опис релізу.