Prologo
Primi Passi
Architettura
Le Basi
- Routing
- Middleware
- Protezione da CSRF
- Controller
- Richieste
- Risposte
- Views
- Blade
- Vite
- URL
- Sessioni
- Validazione
- Errori
- Logging
Approfondimenti
- Artisan
- Broadcasting
- Cache
- Collezioni
- Concorrenza
- Contesto
- Contratti
- Eventi
- File System
- Helpers
- Client HTTP
- Localizzazione
- Notifiche
- Sviluppo di Package
- Processi
- Code
- Rate-limiting
- Stringhe
- Scheduling di Task
Sicurezza
Database
Eloquent ORM
Testing
Package
Contesto
- Introduzione
- Catturare il Contesto
- Recupero del Contesto
- Rimuovere il Contesto
- Contesto Nascosto
- Eventi
Introduzione
Le funzionalità di "context" di Laravel ti permettono di catturare, recuperare e condividere informazioni durante richieste, job e comandi che vengono eseguiti nella tua applicazione. Queste informazioni sono incluse anche nei log scritti dalla tua applicazione, offrendo una comprensione più profonda della cronologia di esecuzione del codice che è avvenuta prima che un log venisse scritto e permettendo di tracciare i flussi di esecuzione in un sistema distribuito.
Come funziona
Il modo migliore per comprendere le capacità di contesto di Laravel è vederle in azione utilizzando le funzionalità di log integrate. Per iniziare, puoi aggiungere informazioni al contesto usando la facade Context
. In questo esempio, useremo un middleware per aggiungere l’URL della richiesta e un ID di traccia unico al contesto ad ogni richiesta in arrivo:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class AddContext
{
/**
* Gestisci una richiesta in arrivo.
*/
public function handle(Request $request, Closure $next): Response
{
Context::add('url', $request->url());
Context::add('trace_id', Str::uuid()->toString());
return $next($request);
}
}
Le informazioni aggiunte al contesto vengono automaticamente aggiunte come metadata a qualsiasi log scritta durante la richiesta. Aggiungere il contesto come metadata permette di differenziare le informazioni passate alle singole voci di log rispetto alle informazioni condivise tramite Context
. Per esempio, immagina di scrivere la seguente voce di log:
Log::info('User authenticated.', ['auth_id' => Auth::id()]);
Il log scritto conterrà l’auth_id
passato alla voce di log, ma conterrà anche l’url
e il trace_id
del contesto come metadata:
User authenticated. {"auth_id":27} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
Le informazioni aggiunte al contesto sono disponibili anche per i job inviati alla coda. Per esempio, immagina di inviare un job ProcessPodcast
alla coda dopo aver aggiunto alcune informazioni al contesto:
// Nel nostro middleware...
Context::add('url', $request->url());
Context::add('trace_id', Str::uuid()->toString());
// Nel nostro controller...
ProcessPodcast::dispatch($podcast);
Quando il job viene inviato, tutte le informazioni attualmente memorizzate nel contesto vengono catturate e condivise con il job. Le informazioni catturate vengono poi ripristinate nel contesto corrente mentre il job viene eseguito. Quindi, se il metodo handle del nostro job scrivesse nel log:
class ProcessPodcast implements ShouldQueue
{
use Queueable;
// ...
/**
* Esegui il job.
*/
public function handle(): void
{
Log::info('Processing podcast.', [
'podcast_id' => $this->podcast->id,
]);
// ...
}
}
La voce di log risultante conterrebbe le informazioni aggiunte al contesto durante la richiesta che ha originariamente inviato il job:
Processing podcast. {"podcast_id":95} {"url":"https://example.com/login","trace_id":"e04e1a11-e75c-4db3-b5b5-cfef4ef56697"}
Anche se ci siamo concentrati sulle funzionalità di log integrate nel contesto di Laravel, la seguente documentazione illustrerà come il contesto permette di condividere informazioni oltre il confine della richiesta HTTP / job in coda e anche come aggiungere dati di contesto nascosti che non vengono riportati nei log.
Catturare il Contesto
Puoi memorizzare informazioni nel contesto corrente usando il metodo add
del facade Context
:
use Illuminate\Support\Facades\Context;
Context::add('key', 'value');
Per aggiungere più elementi contemporaneamente, puoi passare un array associativo al metodo add
:
Context::add([
'first_key' => 'value',
'second_key' => 'value',
]);
Il metodo add
sovrascriverà qualsiasi valore esistente che condivide la stessa chiave. Se desideri aggiungere informazioni al contesto solo se la chiave non esiste già, puoi usare il metodo addIf
:
Context::add('key', 'first');
Context::get('key');
// "first"
Context::addIf('key', 'second');
Context::get('key');
// "first"
Contesto Condizionale
Il metodo when
può essere usato per aggiungere dati al contesto in base a una determinata condizione. La prima closure fornita al metodo when
verrà eseguita se la condizione data è true
, mentre la seconda closure verrà eseguita se la condizione è false
:
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Context;
Context::when(
Auth::user()->isAdmin(),
fn ($context) => $context->add('permissions', Auth::user()->permissions),
fn ($context) => $context->add('permissions', []),
);
Stack
I contesti offrono la possibilità di creare "stack", che sono liste di dati memorizzati nell’ordine in cui vengono aggiunti. Puoi aggiungere informazioni a uno stack invocando il metodo push
:
use Illuminate\Support\Facades\Context;
Context::push('breadcrumbs', 'first_value');
Context::push('breadcrumbs', 'second_value', 'third_value');
Context::get('breadcrumbs');
// [
'first_value',
'second_value',
'third_value',
]
Gli stack possono essere utili per catturare informazioni storiche su una richiesta, come gli eventi che si verificano nell’applicazione. Ad esempio, potresti creare un listener di eventi per pushare su uno stack ogni volta che viene eseguita una query, catturando l’SQL della query e la durata come una tupla:
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Facades\DB;
DB::listen(function ($event) {
Context::push('queries', [$event->time, $event->sql]);
});
Puoi determinare se un valore è presente in uno stack utilizzando i metodi stackContains
e hiddenStackContains
:
if (Context::stackContains('breadcrumbs', 'first_value')) {
//
}
if (Context::hiddenStackContains('secrets', 'first_value')) {
//
}
I metodi stackContains
e hiddenStackContains
accettano anche una closure come secondo argomento, permettendo un maggiore controllo sull’operazione di confronto dei valori:
use Illuminate\Support\Facades\Context;
use Illuminate\Support\Str;
return Context::stackContains('breadcrumbs', function ($value) {
return Str::startsWith($value, 'query_');
});
Recupero del Contesto
Puoi recuperare informazioni dal contesto usando il metodo get
della facade Context
:
use Illuminate\Support\Facades\Context;
$value = Context::get('key');
Il metodo only
può essere utilizzato per ottenere un sottoinsieme delle informazioni nel contesto:
$data = Context::only(['first_key', 'second_key']);
Il metodo pull
può essere utilizzato per recuperare informazioni dal contesto e rimuoverle immediatamente dal contesto:
$value = Context::pull('key');
Se i dati del contesto sono memorizzati in uno stack, puoi estrarre elementi dalla stack usando il metodo pop
:
Context::push('breadcrumbs', 'first_value', 'second_value');
Context::pop('breadcrumbs')
// second_value
Context::get('breadcrumbs');
// ['first_value']
Se vuoi recuperare tutte le informazioni memorizzate nel contesto, puoi chiamare il metodo all
:
$data = Context::all();
Verificare se un elemento esiste
Puoi usare il metodo has
per verificare se il contesto ha un valore memorizzato per una determinata chiave:
use Illuminate\Support\Facades\Context;
if (Context::has('key')) {
// ...
}
Il metodo has
restituirà true
indipendentemente dal valore memorizzato. Ad esempio, una chiave con un valore null
sarà considerata presente:
Context::add('key', null);
Context::has('key');
// true
Rimuovere il Contesto
Il metodo forget
può essere usato per rimuovere una chiave e il suo valore dal contesto corrente:
use Illuminate\Support\Facades\Context;
Context::add(['first_key' => 1, 'second_key' => 2]);
Context::forget('first_key');
Context::all();
// ['second_key' => 2]
Puoi rimuovere più chiavi contemporaneamente fornendo un array al metodo forget
:
Context::forget(['first_key', 'second_key']);
Contesto Nascosto
Context offre la possibilità di memorizzare dati "nascosti". Queste informazioni nascoste non vengono aggiunte ai log e non sono accessibili tramite i metodi di recupero dati documentati sopra. Context fornisce un set diverso di metodi per interagire con le informazioni del contesto nascosto:
use Illuminate\Support\Facades\Context;
Context::addHidden('key', 'value');
Context::getHidden('key');
// 'value'
Context::get('key');
// null
I metodi "hidden" rispecchiano la funzionalità dei metodi non hidden documentati sopra:
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::forgetHidden(/* ... */);
Eventi
Per il contesto, Laravel emette due eventi che ti permettono di intervenire nel processo di idratazione (così come lo svuotamento) del contesto.
Per esempio, immagina che in un middleware della tua applicazione imposti il valore di configurazione app.locale
basandoti sull’header Accept-Language
della richiesta HTTP in arrivo. Gli eventi di Context ti permettono di catturare questo valore durante la richiesta e di ripristinarlo nella coda, assicurando che le notifiche inviate tramite la coda abbiano il valore corretto di app.locale
. Possiamo utilizzare gli eventi di Context e i dati nascosti per ottenere questo, come illustrato nell’esempio di seguito.
Disidratazione
Ogni volta che un job viene messo in coda, i dati nel contesto vengono "disidratati" e catturati insieme al payload del job. Il metodo Context::dehydrating
ti permette di registrare una closure che verrà eseguita durante il processo di disidratazione. All’interno di questa closure, puoi modificare i dati che saranno condivisi con il job in coda.
Quello che si fa di solito è registrare una callback per dehydrating
nel metodo boot
della classe AppServiceProvider
della tua applicazione:
use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;
/**
* Avvia i servizi dell'applicazione.
*/
public function boot(): void
{
Context::dehydrating(function (Repository $context) {
$context->addHidden('locale', Config::get('app.locale'));
});
}
Non dovresti usare la facade
Context
all’interno del callbackdehydrating
, poiché ciò cambierebbe il contesto del processo corrente. Assicurati di usare solo l’istanza passata alla callback.
Hydrated
Ogni volta che un job in coda inizia ad essere eseguito, qualsiasi contesto condiviso con il job verrà "idratato" nel contesto corrente. Il metodo Context::hydrated
ti permette di registrare una closure che verrà invocata durante il processo di idratazione.
Anche qui dovrai registrare le callback hydrated
all’interno del metodo boot
della classe AppServiceProvider
della tua applicazione:
use Illuminate\Log\Context\Repository;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Context;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Context::hydrated(function (Repository $context) {
if ($context->hasHidden('locale')) {
Config::set('app.locale', $context->getHidden('locale'));
}
});
}
Non dovresti usare la facade
Context
all’interno del callbackhydrated
, poiché ciò cambierebbe il contesto del processo corrente. Assicurati di usare solo l’istanza passata alla callback.