Vai al contenuto principale
Per fornire un backend di cache personalizzato da un pacchetto, separa lo Store che implementa il salvataggio dal Repository che espone le API di Laravel. Ciò che registri con Cache::extend() è il modo di creare lo store: Laravel non avvolge automaticamente in un Repository lo Store che restituisci. Questa pagina esamina l’implementazione di Laravel Framework v13.39.0. Per le operazioni di base consulta Cache, per la preparazione del provider consulta Sviluppo di pacchetti.

Separare il nome del driver dal nome dello store

Se esiste una callback di creazione personalizzata corrispondente al driver della configurazione, CacheManager::build() la invoca prima dei driver integrati. Non riutilizzare nomi come redis o array: usa un nome specifico del pacchetto.

Registrare prima del boot degli altri provider

Se un altro pacchetto usa la cache nel proprio boot(), registrare il driver nel tuo boot() potrebbe non arrivare in tempo, a seconda dell’ordine. Seguendo la documentazione ufficiale, in register() registra una callback booting dell’applicazione. L’esempio seguente è minimo e usa l’ArrayStore standard per verificare il percorso di registrazione senza servizi esterni. Collocalo in src/MemoServiceProvider.php del pacchetto Acme\Memo.
Il provider viene caricato, ad esempio, tramite il package auto-discovery. Questo provider, che si limita a registrare l’estensione, non deve essere un provider differito, così da essere caricato sia in HTTP sia in Artisan.
  • Nel normale avvio, la callback booting dell’applicazione viene eseguita dopo il completamento di register() di tutti i provider e prima di ciascun boot(). È diversa da $this->booting() del provider stesso.
  • Alla callback di extend() vengono passati l’applicazione e l’array di configurazione dello store. Colloca la creazione dello Store e le connessioni esterne in questa callback di creazione.
  • Questo momento di registrazione non garantisce il funzionamento per l’uso della cache all’interno di register() di altri provider. Separa la registrazione dei servizi dal loro utilizzo effettivo.
Se ti serve solo la cache in memoria standard, il driver array è sufficiente. Questo esempio serve a illustrare la registrazione e i contratti, e non può essere usato per condivisione tra processi o persistenza. Per un backend personalizzato, sostituisci la creazione di ArrayStore con una classe che implementa Illuminate\Contracts\Cache\Store.
CacheManager::extend() ricollega lo scope delle closure anonime al Manager. Non considerare $this di una closure non static come il provider: come nell’esempio, usa closure static e l’argomento $app.

Aggiungere lo store nell’applicazione che usa il pacchetto

Aggiungilo all’array stores esistente in config/cache.php. Non progettare il pacchetto in modo che sovrascriva il default o l’intero stores dell’applicazione.
Anche quando vuoi renderlo predefinito, in CACHE_STORE indichi il nome dello store catalog, non il nome del driver. Se crei più configurazioni con lo stesso driver, viene generato un Repository distinto per ogni nome di store.

Costruire il Repository da sé

CacheManager::callCustomCreator() restituisce così com’è il valore restituito dalla callback. Se restituisci solo lo Store, anche se get() e put() funzionano, metodi del Repository come remember() non sono disponibili e anche l’interpretazione del TTL cambia. Nell’esempio, repository($store, $config) si occupa di quanto segue.
  • Avvolge lo Store con Illuminate\Cache\Repository.
  • Passa al Repository il nome store che il Manager ha aggiunto alla configurazione.
  • Se è registrato un Dispatcher di eventi e l’opzione events non è disabilitata, imposta il Dispatcher sul Repository.
Non si tratta di un’operazione che applica allo Store tutte le opzioni personalizzate. Destinazione della connessione, prefix, serializzazione e così via vanno applicati nell’implementazione dello Store e nella callback di creazione. Lo stesso vale per serialize, letto dalla configurazione nell’esempio.

Confrontare il contratto Store con l’implementazione attuale

Implementa i metodi richiesti da Illuminate\Contracts\Cache\Store in Laravel 13. Non distribuire come driver funzionante un elenco di metodi vuoti preso da esempi passati. Il Repository tratta null come cache miss. L’implementazione non deve trattare come mancanti anche false o 0. Salvare null non funziona come marcatore per evitare il ricalcolo di remember().

Verificare il TTL passando per il Repository

Repository::put() elabora il TTL prima di passarlo allo Store.
L’ArrayStore dell’esempio, se riceve direttamente 0 secondi in put(), salva senza scadenza. Invece put(..., 0) del Repository elimina la chiave. Testando solo lo Store di basso livello ottenuto con getStore() non verifichi il comportamento delle API chiamate dagli utenti.

Verificare separatamente il supporto ad add, lock e tag

La sola implementazione del contratto Store non garantisce il supporto a tutte le funzionalità della cache.
  • add() atomico: non fa parte del contratto Store. Quando viene chiamato con un TTL positivo e lo Store ha add(), il Repository lo usa. Altrimenti ricorre a un fallback con get() e put(), quindi il solo wrapping nel Repository non garantisce l’atomicità. Con un backend condiviso, implementalo con un’operazione atomica di quel backend e verifica anche l’esecuzione concorrente.
  • Lock: lock() e restoreLock() di Illuminate\Contracts\Cache\LockProvider sono un contratto separato. Verifica acquisizione, corrispondenza del proprietario, scadenza e il fatto che non venga rilasciato il lock di un altro proprietario. I lock di ArrayStore dell’esempio non forniscono mutua esclusione tra processi diversi.
  • Tag: il Repository controlla se lo Store ha tags(). In generale serve un’implementazione basata su TaggableStore o simili: il contratto Store da solo non li abilita automaticamente.
Non considerare flush() come una cancellazione parziale che rispetta il prefix della configurazione. Verifica l’ambito della cancellazione sul backend condiviso e spiegalo agli utenti, incluso php artisan cache:clear.

Distinguere la conservazione delle istanze dalla cancellazione dei dati

CacheManager è un singleton e conserva anche il risultato di store('catalog') per ogni nome di store. La sola modifica della configurazione o una nuova registrazione con extend() non aggiorna i Repository già creati.
$same è lo stesso oggetto di $before. Con il recupero successivo a forgetDriver(), $after viene creato dalla nuova configurazione. forgetDriver() non cancella tutto il backend. Tuttavia, poiché l’ArrayStore dell’esempio conserva i dati all’interno dell’istanza, i vecchi dati non sono visibili dal nuovo Store. Inoltre, i riferimenti al vecchio Repository conservati da $before lato chiamante o dal singleton cache.store già risolto non vengono sostituiti. In produzione, aggiorna la cache della configurazione seguendo il normale deploy e riavvia i worker di lunga durata. Tieni conto anche del ciclo di vita dei servizi e non trattare una nuova risoluzione come un aggiornamento simultaneo di tutti i processi.

Testare il percorso di creazione, non solo il mock della facade

Nell’ambiente di test dei pacchetti, passa per il Manager e il Repository reali. Il seguente esempio con Testbench verifica che il driver sia utilizzabile anche nel boot() di un altro provider registrato prima. Nell’esempio si salva un array senza modificare la restrizione di deserializzazione cache.serializable_classes e si controlla anche il valore grezzo salvato con ArrayStore::all(false).
Per un backend personalizzato, verifica inoltre la scadenza, le chiavi mancanti in many(), l’andata e ritorno di false e 0, touch(), i guasti di connessione, gli aggiornamenti concorrenti, il prefix e l’ambito della cancellazione. Il semplice successo di Cache::shouldReceive('get') non è una prova della registrazione del driver né della compatibilità con il servizio esterno.

Checklist per la manutenzione a lungo termine

  • Rendi parte della specifica pubblica il nome del driver, la configurazione dello store, il formato di serializzazione, le eccezioni e i valori restituiti in caso di fallimento.
  • Verifica i metodi del contratto Store e le funzionalità opzionali come tag e lock per ogni versione di Laravel supportata.
  • Verifica che il driver venga registrato anche in un avvio pulito con la cache della configurazione attiva e che sia utilizzabile dal boot() di altri provider.
  • Quando modifichi la compatibilità dei dati salvati, comunicalo includendo la strategia di migrazione o eliminazione delle chiavi esistenti.
  • Distingui la conservazione delle istanze nel processo, i dati del backend e la cache della configurazione.

Fonti primarie consultate

Ultima modifica il 29 ottobre 2026