---
title: "Laravel Context: прихована інформація, яка супроводжує ваш код"
url: https://laravelukraine.com/blog/laravel-context-prixovana-informaciia-iaka-suprovodzuje-vas-kod
date: 2026-10-29
source: https://dev.to/othmane_nemli/laravel-context-the-hidden-information-that-follows-your-code-1mo8
---

# Laravel Context: прихована інформація, яка супроводжує ваш код

Працюючи з Laravel, ми зазвичай передаємо дані явно:

```php
$user = auth()->user();
UserExampleService::process($user);
```

Або додаємо інформацію безпосередньо до запису логу:

```php
Log::info('Processing order', [
  'order_id' => $order->id,
]);
```

А що робити, коли деяка інформація має бути доступна протягом усього поточного виконання?

Наприклад:

- trace ID;
- поточний URL;
- ідентифікатор орендаря (tenant ID);
- назва операції;
- інформація, потрібна для логування;
- інформація, яка має переходити в завдання черги.

Саме тут стає цікавою можливість Laravel під назвою **Context**.

### Що таке Laravel Context?

Laravel Context - це місце для інформації, яка належить до **поточного виконання** вашого застосунку.

Ви додаєте дані:

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

Context::add('trace_id', 'abc-123');
```

І отримуєте їх в іншому місці:

```php
$traceId = Context::get('trace_id');
```

Найцікавіше те, що вам не доводиться передавати `$traceId` через кожен метод.

Інформація живе в поточному контексті, і нею можуть користуватися інші частини застосунку.

Сам Laravel описує Context як спосіб збирати, отримувати й поширювати інформацію в межах запитів, завдань і команд. До того ж ця інформація інтегрована з логами застосунку.

### Найпростіший приклад

Уявімо `middleware`, що генерує trace ID для кожного запиту:

```php
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;

Context::add('trace_id', Str::uuid()->toString());
```

А тепер десь глибоко в застосунку:

```php
$traceId = Context::get('trace_id');
```

- Жодного параметра в контролері.
- Жодного параметра в сервісі.
- Жодної глобальної змінної.

Дані просто є частиною поточного контексту.

### Справді корисне: логування

Context стає особливо корисним у поєднанні з логуванням.

Наприклад:

```php
Context::add([
  'trace_id' => Str::uuid()->toString(),
  'url' => $request->url(),
]);
```

А потім:

```php
Log::info('User authenticated', [
  'user_id' => $user->id,
]);
```

Запис логу може містити і дані, передані в лог явно, і дані, збережені в Context:

```text
User authenticated
{
  "user_id": 27,
  "trace_id": "e04e1a11-...",
  "url": "https://dev.to/login"
}
```

У документації Laravel 13 дані контексту не зливаються з даними запису в один масив, а додаються окремим блоком метаданих, щоб їх можна було відрізнити: `User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-..."}`.

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

### А ось тут Context стає по-справжньому цікавим

Уявімо, що відбувається таке:

```text
HTTP-запит
  │
  ├── Контролер
  │     │
  │     └── Сервіс
  │           │
  │           ├── контекст
  │           └── Dispatch завдання
  │
  ▼
Дані завдання (контекст зібрано, дегідратовано й збережено в даних)
  │
  ▼
Черга
  │
  ▼
Воркер
  │
  ▼ гідратація -> відновлення контексту
  │
  ▼
Виконання завдання
```

Зазвичай HTTP-запит і завдання в черзі - це два різні виконання.

Тож звідки завданню знати про контекст, створений під час початкового запиту?

Laravel бере це на себе: коли завдання відправляється в чергу, Laravel збирає поточний контекст і зберігає його разом із даними завдання.

Коли воркер починає обробляти завдання, цей контекст відновлюється в поточному виконанні. Laravel називає ці два етапи **дегідратацією** (dehydrating) і **гідратацією** (hydrating).

Тож ось це:

```php
Context::add('trace_id', 'abc-123');

ProcessPodcast::dispatch($podcast);
```

може привести до того, що завдання побачить:

```php
Context::get('trace_id');
// "abc-123"
```

А отже:

```php
Log::info('Processing podcast');
```

все ще міститиме початкову інформацію для трасування. Це напрочуд потужна можливість для налагодження розподілених процесів.

Тобто Context - не глобальна змінна, спільна для кількох процесів.

#### Найцікавіше

Воркер черги в Laravel - це довготривалий процес: він обробляє одне завдання, потім інше:

```plaintext
Воркер
  │
  ├── Завдання A
  │
  ├── Завдання B
  │
  ├── Завдання C
  │
  └── Завдання D
```

Якби Context просто зберігався глобально й ніколи не очищувався, могло б вийти таке:

```plaintext
Завдання A
trace_id = abc-123

Завдання B
trace_id = abc-123  (а це погано)
```

Тому Laravel керує життєвим циклом навколо воркера (`registerWorker()`). [Логіка скидання](https://github.com/laravel/framework/blob/13.x/src/Illuminate/Queue/QueueServiceProvider.php#L247) воркера в сервіс-провайдері черги серед інших операцій очищення скидає спільний контекст логування й очищає scoped-екземпляри між завданнями.

### Контекст може бути прихованим

Не все, що ви кладете в Context, має з'являтися в логах. Саме для цього Laravel має **прихований контекст**:

```php
Context::addHidden('internal_hidden_id', 123);

Context::getHidden('internal_hidden_id');
// 123
```

Але:

```php
Context::get('internal_hidden_id');
// null
```

Прихований контекст не додається до записів логу й має окремий API.

Отже, маємо два види контексту:

```text
Context
├── Звичайні дані
│   └── Можуть потрапляти в логи
│
└── Приховані дані
    └── Доступні застосунку, але не потрапляють у логи
```

Це розрізнення особливо корисне, коли контекст містить внутрішню інформацію, якій не місце в логах застосунку.

Сам Laravel теж користується прихованим контекстом.

У [CallQueuedHandler](https://github.com/laravel/framework/blob/13.x/src/Illuminate/Queue/CallQueuedHandler.php#L357-L361) Laravel може зберігати таку інформацію:

```properties
laravel_unique_job_cache_store
laravel_unique_job_key
laravel_unique_job_lock_owner
```

*Навіщо?* Буває, що Laravel не може десеріалізувати завдання з черги, бо модель, на яку воно посилається, відсутня.

У такому разі Laravel однаково потрібно достатньо інформації, щоб зняти блокування унікального завдання.

Обробник бере ці значення з прихованого контексту й використовує їх, щоб відновити або зняти блокування.

### У контексту може бути життєвий цикл

Laravel також дає змогу втрутитися в момент, коли контекст перетинає межу черги.

Зазвичай колбеки `dehydrating` і `hydrated` реєструють у методі `boot` класу `AppServiceProvider` вашого застосунку.

Наприклад:

```php
Context::dehydrating(function (Repository $context) {
  // Підготувати контекст, перш ніж його буде прикріплено до завдання
  $context->addHidden('locale', Config::get('app.locale'));
});
```

І:

```php
Context::hydrated(function (Repository $context) {
  // Відновити або використати контекст, коли завдання стартує
  if ($context->hasHidden('locale')) {
    Config::set('app.locale', $context->getHidden('locale'));
  }
});
```

Тобто Context - це не просто статичний масив, що десь лежить.

Laravel дає вам хуки навколо переходу з одного середовища виконання в інше.

Одна важлива деталь із документації Laravel: усередині цих колбеків слід змінювати репозиторій `$context`, переданий у колбек, а не викликати сам фасад `Context`.

### Context - це більше, ніж сховище ключ-значення

Окрім `add()` і `get()`, є й інші корисні можливості.

Наприклад, можна зберігати стек:

```php
Context::push('breadcrumbs', 'checkout');
Context::push('breadcrumbs', 'payment');

Context::get('breadcrumbs');
```

Можна отримати значення й одразу його видалити:

```php
$value = Context::pull('key');
```

Або запам'ятати значення, лише якщо його ще немає:

```php
$permissions = Context::remember(
  'user-permissions',
  fn () => $user->permissions,
);
```

Laravel також має `has`, `missing`, `forget`, `only`, `except` і відповідні методи для прихованого контексту.

Усе це не знадобиться вам щодня. Головне - розуміти, для чого Context призначений.

### Коли ним користуватися?

Context особливо доречний, коли інформація належить до **потоку виконання**, а не до конкретної функції.

Наприклад:

```text
Запит
  │
  ├── Middleware
  │
  ├── Контролер
  │
  ├── Сервіс
  │
  ├── Подія
  │
  ├── Завдання черги
  │
  └── Логи
```

Значення на кшталт:

```text
trace_id = abc-123
```

описує всю операцію, а не один конкретний метод. Саме тут Context і розкривається: замість того щоб скрізь писати

```php
processOrder($order, $traceId);
```

можна тримати інформацію для трасування в Context:

```php
Context::get('trace_id');
```

і дозволити фреймворку переносити її крізь увесь потік виконання.

### Одне важливе правило

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

> **Аргументи описують, що потрібно функції. Контекст описує, що відбувається навколо поточного виконання.**

Наприклад:

```php
processOrder($order);
```

має сенс, бо `$order` - це вхідні дані операції.

Але й:

```php
Context::add('trace_id', $traceId);
```

має сенс, бо trace ID описує виконання, в межах якого відбувається ця операція.

### То зрештою це ще одне сховище ключ-значення?

Сам API простий:

```php
Context::add(...)
Context::get(...)
```

Цікаве те, що Laravel робить із цією інформацією.

Він може:

- додавати її до логів;
- переносити її з HTTP-запиту в завдання черги;
- відновлювати її, коли завдання стартує;
- тримати приховані дані окремо від даних для логів;
- давати застосунку хуки навколо цього перенесення.

Тож наступного разу, коли побачите:

```php
Context::add('trace_id', $traceId);
```

не думайте про це як про ще одне сховище ключ-значення в Laravel. Думайте про це як про **метадані, прикріплені до потоку виконання** - саме це й робить Laravel Context набагато цікавішим, ніж здається на перший погляд.

Джерело: [документація Laravel про Context](https://laravel.com/docs/13.x/context).
