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
Cache
- Introduzione
- Configurazione
- Utilizzo della Cache
- Blocchi Atomici
- Aggiungere driver di cache personalizzati
- Events
Introduzione
Alcune operazioni di recupero o elaborazione dei dati eseguite dalla tua applicazione potrebbero richiedere molte risorse CPU o impiegare diversi secondi per essere completate. In questi casi, è comune memorizzare nella cache i dati recuperati per un certo periodo in modo che possano essere ottenuti rapidamente nelle richieste successive per gli stessi dati. I dati memorizzati nella cache sono solitamente conservati in un data store molto veloce come Memcached o Redis.
Fortunatamente, Laravel offre un’API espressiva e unificata per diversi sistemi di cache, permettendoti di sfruttare il loro recupero dati super veloce e velocizzare la tua applicazione web.
Configurazione
Il file di configurazione della cache della tua applicazione si trova in config/cache.php
. In questo file, puoi specificare quale store di cache vuoi usare di default in tutta l’applicazione. Laravel supporta backend di cache popolari come Memcached, Redis, DynamoDB e database relazionali. Inoltre, è disponibile un driver per la cache basata su file, mentre i driver array
e "null" offrono soluzioni comode per i tuoi test automatici.
Il file di configurazione della cache contiene anche diverse altre opzioni che puoi esaminare. Per impostazione predefinita, Laravel è configurato per usare il driver di cache database
, che memorizza gli oggetti serializzati nella database della tua applicazione.
Prerequisiti del Driver
Database
Quando utilizzi il driver di cache database
, hai bisogno di una tabella nel database per memorizzare i dati della cache. Di solito, questa tabella è inclusa nella migrazione predefinita di Laravel 0001_01_01_000001_create_cache_table.php
migrazione del database. Tuttavia, se la tua applicazione non include questa migrazione, puoi usare il comando Artisan make:cache-table
per crearla:
php artisan make:cache-table
php artisan migrate
Memcached
Usare il driver Memcached richiede l’installazione del pacchetto Memcached PECL. Puoi elencare tutti i tuoi server Memcached nel file di configurazione config/cache.php
. Questo file contiene già una voce memcached.servers
per iniziare:
'memcached' => [
// ...
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
Se necessario, puoi impostare l’opzione host
su un percorso di socket UNIX. In questo caso, l’opzione port
deve essere impostata su 0
:
'memcached' => [
// ...
'servers' => [
[
'host' => '/var/run/memcached/memcached.sock',
'port' => 0,
'weight' => 100
],
],
],
Redis
Prima di usare una cache Redis con Laravel, devi installare l’estensione PHP PhpRedis tramite PECL oppure installare il pacchetto predis/predis
(~2.0) tramite Composer. Laravel Sail include già questa estensione. Inoltre, le piattaforme di deployment ufficiali di Laravel come Laravel Forge e Laravel Vapor hanno l’estensione PhpRedis installata di default.
Per maggiori informazioni sulla configurazione di Redis, consulta la sua pagina della documentazione Laravel.
DynamoDB
Prima di usare il driver cache DynamoDB, devi creare una tabella DynamoDB per memorizzare tutti i dati in cache. Di solito, questa tabella dovrebbe chiamarsi cache
. Tuttavia, dovresti nominare la tabella in base al valore della configurazione stores.dynamodb.table
nel file di configurazione cache
. Il nome della tabella può essere impostato anche tramite la variabile di ambiente DYNAMODB_CACHE_TABLE
.
Questa tabella dovrebbe avere anche una chiave di partizione di tipo stringa con un nome che corrisponde al valore dell’elemento di configurazione stores.dynamodb.attributes.key
nel file di configurazione cache
della tua applicazione. Di default, la chiave di partizione dovrebbe chiamarsi key
.
In genere, DynamoDB non rimuove proattivamente gli elementi scaduti da una tabella. Pertanto, dovresti abilitare Time to Live (TTL) sulla tabella. Quando configuri le impostazioni TTL della tabella, dovresti impostare il nome dell’attributo TTL su expires_at
.
Successivamente, installa l’SDK AWS in modo che la tua applicazione Laravel possa comunicare con DynamoDB:
composer require aws/aws-sdk-php
Inoltre, dovresti assicurarti che vengano forniti valori per le opzioni di configurazione del negozio cache DynamoDB. Tipicamente, queste opzioni, come AWS_ACCESS_KEY_ID
e AWS_SECRET_ACCESS_KEY
, dovrebbero essere definite nel file di configurazione .env
della tua applicazione:
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
MongoDB
Se stai usando MongoDB, un driver di cache mongodb
è fornito dal pacchetto ufficiale mongodb/laravel-mongodb
e può essere configurato usando una connessione al database mongodb
. MongoDB supporta indici TTL, che possono essere usati per cancellare automaticamente gli elementi di cache scaduti.
Per maggiori informazioni sulla configurazione di MongoDB, consulta la documentazione MongoDB Cache and Locks.
Utilizzo della Cache
Ottenere un’istanza di Cache
Per ottenere un’istanza di uno store di cache, puoi utilizzare la facade Cache
, che useremo in tutta questa documentazione. La facade Cache
offre un accesso comodo e conciso alle implementazioni sottostanti dei contratti di cache di Laravel:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Mostra una lista di tutti gli utenti dell'applicazione.
*/
public function index(): array
{
$value = Cache::get('key');
return [
// ...
];
}
}
Accesso a più cache store
Usando la facciata Cache
, puoi accedere a diversi cache store tramite il metodo store
. La chiave passata al metodo store
deve corrispondere a uno degli store elencati nell’array di configurazione stores
nel tuo file di configurazione cache
:
$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 600); // 10 minuti
Recuperare Elementi dalla Cache
Il metodo get
del facade Cache
viene utilizzato per recuperare elementi dalla cache. Se l’elemento non esiste nella cache, verrà restituito null
. Se vuoi, puoi passare un secondo argomento al metodo get
specificando il valore di default che desideri venga restituito se l’elemento non esiste:
$value = Cache::get('key');
$value = Cache::get('key', 'default');
Puoi anche passare una closure come valore di default. Il risultato della closure verrà restituito se l’elemento specificato non esiste nella cache. Passare una closure ti permette di ritardare il recupero dei valori di default da un database o da un altro servizio esterno:
$value = Cache::get('key', function () {
return DB::table(/* ... */)->get();
});
Verificare se un Elemento Esiste
Il metodo has
può essere usato per verificare se un elemento esiste nella cache. Questo metodo restituirà anche false
se l’elemento esiste ma il suo valore è null
:
if (Cache::has('key')) {
// ...
}
Incremento / Decremento dei Valori
I metodi increment
e decrement
possono essere usati per modificare il valore degli elementi interi nella cache. Entrambi questi metodi accettano un secondo argomento opzionale che indica l’ammontare di incremento o decremento del valore dell’elemento:
// Inizializza il valore se non esiste...
Cache::add('key', 0, now()->addHours(4));
// Incrementa o decrementa il valore...
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
Recupera e Memorizza
A volte potresti voler recuperare un elemento dalla cache, ma anche memorizzare un valore predefinito se l’elemento richiesto non esiste. Ad esempio, potresti voler recuperare tutti gli utenti dalla cache oppure, se non esistono, recuperarli dal database e aggiungerli alla cache. Puoi farlo usando il metodo Cache::remember
:
$value = Cache::remember('users', $seconds, function () {
return DB::table('users')->get();
});
Se l’elemento non esiste nella cache, la closure passata al metodo remember
verrà eseguita e il suo risultato sarà inserito nella cache.
Puoi usare il metodo rememberForever
per recuperare un elemento dalla cache o memorizzarlo indefinitamente se non esiste:
$value = Cache::rememberForever('users', function () {
return DB::table('users')->get();
});
Stale While Revalidate
Quando si utilizza il metodo Cache::remember
, alcuni utenti potrebbero riscontrare tempi di risposta lenti se il valore memorizzato nella cache è scaduto. Per certi tipi di dati, può essere utile permettere di servire dati parzialmente obsoleti mentre il valore della cache viene ricalcolato in background, evitando che alcuni utenti sperimentino tempi di risposta lenti durante il ricalcolo dei valori memorizzati. Questo è spesso chiamato pattern "stale-while-revalidate" e il metodo Cache::flexible
fornisce un’implementazione di questo pattern.
Il metodo flexible
accetta un array che specifica per quanto tempo il valore della cache è considerato “fresco” e quando diventa “stale”. Il primo valore nell’array rappresenta il numero di secondi in cui la cache è considerata fresca, mentre il secondo valore definisce per quanto tempo può essere servita come dato obsoleto prima che sia necessario un ricalcolo.
Se viene fatta una richiesta durante il periodo fresco (prima del primo valore), la cache viene restituita immediatamente senza ricalcolo. Se viene fatta una richiesta durante il periodo stale (tra i due valori), viene servito all’utente il valore obsoleto e viene registrata una deferred function per aggiornare il valore della cache dopo che la risposta è stata inviata all’utente. Se viene fatta una richiesta dopo il secondo valore, la cache è considerata scaduta e il valore viene ricalcolato immediatamente, il che può comportare una risposta più lenta per l’utente:
$value = Cache::flexible('users', [5, 10], function () {
return DB::table('users')->get();
});
Recupera ed Elimina
Se hai bisogno di recuperare un elemento dalla cache e poi eliminarlo, puoi usare il metodo pull
. Come il metodo get
, verrà restituito null
se l’elemento non esiste nella cache:
$value = Cache::pull('key');
$value = Cache::pull('key', 'default');
Memorizzare Elementi nella Cache
Puoi usare il metodo put
sulla facade Cache
per memorizzare elementi nella cache:
Cache::put('key', 'value', $seconds = 10);
Se il tempo di memorizzazione non viene passato al metodo put
, l’elemento sarà memorizzato indefinitamente:
Cache::put('key', 'value');
Invece di passare il numero di secondi come intero, puoi anche passare un’istanza di DateTime
che rappresenta il tempo di scadenza desiderato per l’elemento memorizzato:
Cache::put('key', 'value', now()->addMinutes(10));
Memorizza solo se assente
Il metodo add
aggiunge l’elemento alla cache solo se non esiste già nel cache store. Il metodo restituirà true
se l’elemento viene effettivamente aggiunto alla cache. Altrimenti, restituirà false
. Il metodo add
è un’operazione atomica:
Cache::add('key', 'value', $seconds);
Memorizzare gli Elementi per Sempre
Il metodo forever
può essere usato per memorizzare un elemento nella cache in modo permanente. Poiché questi elementi non scadono, devono essere rimossi manualmente dalla cache utilizzando il metodo forget
:
Cache::forever('key', 'value');
Se stai usando il driver Memcached, gli elementi memorizzati "per sempre" potrebbero essere rimossi quando la cache raggiunge il suo limite di dimensione.
Rimuovere Elementi dalla Cache
Puoi rimuovere elementi dalla cache usando il metodo forget
:
Cache::forget('key');
Puoi anche rimuovere elementi fornendo un numero di secondi di scadenza pari a zero o negativo:
Cache::put('key', 'value', 0);
Cache::put('key', 'value', -5);
Puoi svuotare l’intera cache usando il metodo flush
:
Cache::flush();
Svuotare la cache non rispetta il "prefisso" configurato e rimuoverà tutte le voci dalla cache. Considera attentamente questa operazione quando svuoti una cache condivisa da altre applicazioni.
L’Helper Cache
Oltre a usare il facade Cache
, puoi anche utilizzare la funzione globale cache
per recuperare e memorizzare dati nella cache. Quando la funzione cache
viene chiamata con un solo argomento di tipo stringa, restituirà il valore della chiave specificata:
$value = cache('key');
Se fornisci un array di coppie chiave / valore e un tempo di scadenza alla funzione, essa memorizzerà i valori nella cache per la durata specificata:
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));
Quando la funzione cache
viene chiamata senza argomenti, restituisce un’istanza dell’implementazione di Illuminate\Contracts\Cache\Factory
, permettendoti di chiamare altri metodi di caching:
cache()->remember('users', $seconds, function () {
return DB::table('users')->get();
});
Durante il test delle chiamate alla funzione globale
cache
, puoi usare il metodoCache::shouldReceive
proprio come se stessi testando il facade.
Blocchi Atomici
Per utilizzare questa funzionalità, la tua applicazione deve usare il driver di cache
memcached
,redis
,dynamodb
,database
,file
oarray
come driver di cache predefinito. Inoltre, tutti i server devono comunicare con lo stesso server di cache centrale.
Gestire i Lock
I lock atomici permettono di gestire lock distribuiti senza preoccuparsi delle condizioni di race. Ad esempio, Laravel Forge utilizza lock atomici per garantire che solo un’attività remota venga eseguita su un server alla volta. Puoi creare e gestire i lock usando il metodo Cache::lock
:
use Illuminate\Support\Facades\Cache;
$lock = Cache::lock('foo', 10);
if ($lock->get()) {
// Lock acquisito per 10 secondi...
$lock->release();
}
Il metodo get
accetta anche una closure. Dopo che la closure viene eseguita, Laravel rilascerà automaticamente il lock:
Cache::lock('foo', 10)->get(function () {
// Lock acquisito per 10 secondi e rilasciato automaticamente...
});
Se il lock non è disponibile al momento della richiesta, puoi istruire Laravel ad aspettare per un numero specificato di secondi. Se il lock non può essere acquisito entro il limite di tempo specificato, verrà lanciata un’Illuminate\Contracts\Cache\LockTimeoutException
:
use Illuminate\Contracts\Cache\LockTimeoutException;
$lock = Cache::lock('foo', 10);
try {
$lock->block(5);
// Lock acquisito dopo aver atteso al massimo 5 secondi...
} catch (LockTimeoutException $e) {
// Impossibile acquisire il lock...
} finally {
$lock->release();
}
L’esempio sopra può essere semplificato passando una closure al metodo block
. Quando una closure viene passata a questo metodo, Laravel tenterà di acquisire il lock per il numero di secondi specificato e rilascerà automaticamente il lock una volta eseguita la closure:
Cache::lock('foo', 10)->block(5, function () {
// Lock acquisito dopo aver atteso al massimo 5 secondi...
});
Gestione dei Lock tra Processi
A volte, potresti voler acquisire un lock in un processo e rilasciarlo in un altro processo. Ad esempio, potresti acquisire un lock durante una richiesta web e desiderare rilasciare il lock alla fine di un job in coda attivato da quella richiesta. In questo scenario, dovresti passare il "owner token" del lock al job in coda affinché il job possa re-instanziare il lock usando il token fornito.
Nell’esempio seguente, dispatcheremo un job in coda se un lock viene acquisito con successo. Inoltre, passeremo il "owner token" del lock al job in coda tramite il metodo owner
del lock:
$podcast = Podcast::find($id);
$lock = Cache::lock('processing', 120);
if ($lock->get()) {
ProcessPodcast::dispatch($podcast, $lock->owner());
}
All’interno del job ProcessPodcast
della nostra applicazione, possiamo ripristinare e rilasciare il lock usando il "owner token":
Cache::restoreLock('processing', $this->owner)->release();
Se desideri rilasciare un lock senza rispettare il suo owner attuale, puoi usare il metodo forceRelease
:
Cache::lock('processing')->forceRelease();
Aggiungere driver di cache personalizzati
Scrivere il Driver
Per creare il nostro driver di cache personalizzato, dobbiamo prima implementare il Illuminate\Contracts\Cache\Store
contratto. Quindi, un’implementazione della cache con MongoDB potrebbe essere simile a questa:
<?php
namespace App\Extensions;
use Illuminate\Contracts\Cache\Store;
class MongoStore implements Store
{
public function get($key) {}
public function many(array $keys) {}
public function put($key, $value, $seconds) {}
public function putMany(array $values, $seconds) {}
public function increment($key, $value = 1) {}
public function decrement($key, $value = 1) {}
public function forever($key, $value) {}
public function forget($key) {}
public function flush() {}
public function getPrefix() {}
}
Dobbiamo solo implementare ciascuno di questi metodi utilizzando una connessione MongoDB. Per un esempio di come implementare ciascuno di questi metodi, dai un’occhiata a Illuminate\Cache\MemcachedStore
nel codice sorgente del framework Laravel. Una volta completata la nostra implementazione, possiamo finire la registrazione del driver personalizzato chiamando il metodo extend
della facade Cache
:
Cache::extend('mongo', function (Application $app) {
return Cache::repository(new MongoStore);
});
Se ti stai chiedendo dove mettere il codice del tuo driver di cache personalizzato, puoi creare uno spazio dei nomi
Extensions
nella directoryapp
. Tuttavia, ricorda che Laravel non ha una struttura applicativa rigida e puoi organizzare la tua applicazione come preferisci.
Registrare il Driver
Per registrare il driver cache personalizzato con Laravel, utilizzeremo il metodo extend
sulla facade Cache
. Poiché altri service provider potrebbero tentare di leggere valori cache all’interno del loro metodo boot
, registreremo il nostro driver personalizzato all’interno di un callback booting
. Usando il callback booting
, possiamo assicurarci che il driver personalizzato sia registrato poco prima che il metodo boot
venga chiamato sui service provider della nostra applicazione, ma dopo che il metodo register
sia stato chiamato su tutti i service provider. Registreremo il nostro callback booting
all’interno del metodo register
della classe App\Providers\AppServiceProvider
della nostra applicazione:
<?php
namespace App\Providers;
use App\Extensions\MongoStore;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$this->app->booting(function () {
Cache::extend('mongo', function (Application $app) {
return Cache::repository(new MongoStore);
});
});
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// ...
}
}
Il primo argomento passato al metodo extend
è il nome del driver. Questo corrisponderà alla tua opzione driver
nel file di configurazione config/cache.php
. Il secondo argomento è una closure che dovrebbe restituire un’istanza di Illuminate\Cache\Repository
. La closure riceverà un’istanza $app
, che è un’istanza del service container.
Una volta che la tua estensione è registrata, aggiorna la variabile d’ambiente CACHE_STORE
o l’opzione default
nel file di configurazione config/cache.php
della tua applicazione con il nome della tua estensione.
Events
Per eseguire del codice ad ogni operazione di cache, puoi ascoltare vari eventi emessi dalla cache:
Nome Evento |
---|
Illuminate\Cache\Events\CacheHit |
Illuminate\Cache\Events\CacheMissed |
Illuminate\Cache\Events\KeyForgotten |
Illuminate\Cache\Events\KeyWritten |
Per migliorare le prestazioni, puoi disabilitare gli eventi della cache impostando l’opzione di configurazione events
su false
per uno specifico store della cache nel file di configurazione config/cache.php
della tua applicazione:
'database' => [
'driver' => 'database',
// ...
'events' => false,
],