---
title: "Валідація ключів масиву в Laravel: нове правило array_keys"
url: https://laravelukraine.com/blog/validaciia-kliuciv-masivu-v-laravel-nove-pravilo-array-keys
date: 2026-08-08
source: https://laravel-news.com/laravel-array-keys-validation-rule?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+laravelnews
---

# Валідація ключів масиву в Laravel: нове правило array_keys

Ендпоінти, які приймають набір опцій, мають тихий режим відмови. Клієнт надсилає `?filter[stat us]=draft` з друкарською помилкою, ваш код читає `$filters['status']`, нічого не знаходить і повертає нефільтрований список. Ніхто не отримує помилку, відповідь виглядає нормально, а баг спливає пізніше як "фільтр іноді не працює".

Laravel 13.24 додає правило валідації `array_keys` саме для цього: масив може містити ці ключі і нічого більше, з повідомленням про помилку, яке вказує, що пішло не так.

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

Працюють як builder-форма, так і рядкова:

```php
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` все ще покриває це, і обидва правила можна комбінувати:

```php
'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. |

Перше повідомлення активно вводить в оману, оскільки значення *є* масивом. Також немає placeholder для проблемних ключів, тому неможливо написати власне повідомлення, яке їх називає.

Обидва правила звітуються окремо в `$validator->failed()` як `Array` та `ArrayKeys`, тому можна застосовувати обидва, коли потрібно розрізняти кожну помилку:

```php
'filter' => ['array', Rule::arrayKeys(['status', 'author', 'tag'])],
```

`Rule::array()` та його повідомлення залишаються незмінними, тому нічого з уже написаного коду не поводиться інакше.

## Вказування проблемних ключів

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

```php
$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.

## Приклад фільтрованого ендпоінту

Об'єднуючи все разом на маршруті, який приймає фільтри, сортування та include з власним набором дозволених ключів:

```php
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.',
        ];
    }
}
```

Контролер може тоді читати набір фільтрів без захисних перевірок, оскільки все, що до нього дійшло, є ключем, який ви вказали:

```php
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-колонки

Те саме правило корисне при записі даних, де колонка налаштувань або уподобань схильна накопичувати все, що випадково надіслав frontend:

```php
'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'])],
```

Перейменоване поле frontend тепер явно падає під час розгортання замість того, щоб записувати ключ, який ніхто не читає, у кожен рядок.

## Деталі, які варто знати

Кілька особливостей поведінки, які не очевидні з повідомлення:

- **Значення, яке не є масивом, не проходить правило.** Передача `'filter' => 'draft'` створює повідомлення "must only contain the following keys", яке читається дивно для рядка. Поєднуйте його з `array`, коли вхідні дані можуть взагалі не бути масивом, щоб помилка типу повідомлялася окремо.

- **Правило потребує принаймні одного ключа.** Написання `'array_keys'` без ключів викликає `InvalidArgumentException` під час виконання валідації, а не провалює поле. Немає форми "не дозволяти нічого"; цей випадок покриває `prohibited`.

- **Ключі можуть походити з будь-якого `Arrayable`.** Працюють колекція або список на основі enum, а backed enum розв'язуються до їхніх значень:

```php
Rule::arrayKeys(FilterKey::cases());
Rule::arrayKeys(collect(config('filters.allowed')));
```

- **Варіативна форма теж працює.** `Rule::arrayKeys('status', 'author')` еквівалентна передачі масиву, що зручно для inline-використання.

Правило було внесено [@nebarg](https://github.com/nebarg) у [#60918](https://github.com/laravel/framework/pull/60918).
