callAfterResolving() del service provider serve proprio a combinare questi due comportamenti.
Questa pagina esamina l’implementazione di Laravel Framework v13.35.0 e approfondisce l’estensione da boot() descritta in Sviluppo di pacchetti Laravel. Non si tratta di un hook che attende il completamento dell’avvio dell’intera applicazione, ma di un hook sulla risoluzione di un servizio specifico.
Attendere se non risolto, eseguire subito se già risolto
ServiceProvider::callAfterResolving() esegue due passaggi.
- Registra la callback in
afterResolving()del container. - Se
resolved()è true, ottiene il servizio conmake()e invoca la callback anche immediatamente.
make() sul servizio. In una risoluzione normale, il container costruisce l’oggetto, applica gli extender, invoca le callback resolving e infine le callback afterResolving.
Nel normale recupero di un singleton, il container restituisce anticipatamente l’istanza salvata, quindi
afterResolving non viene attivato a ogni recupero. Il punto importante è che registrando semplicemente afterResolving() si perde la configurazione dei singleton creati prima della registrazione.
Aggiungere una regola alla Factory di validazione
Come esempio di un pacchetto che fornisce una propria regola per stringhe, registriamo la regola dopo la risoluzione divalidator. Il ValidationServiceProvider ufficiale registra questa chiave come singleton e il provider stesso supporta il caricamento differito.
extend() è l’API di registrazione delle regole di Illuminate\Validation\Factory, ed è un metodo diverso dall’extend() del container descritto più avanti. Poiché le normali regole personalizzate possono non essere eseguite con valori vuoti, l’obbligatorietà va indicata con required. Per la progettazione dettagliata delle regole, consulta Regole di validazione personalizzate.
Factory::extend() sovrascrive l’elemento dell’array con lo stesso nome di regola, quindi registrare di nuovo la stessa logica, come in questo esempio, non aggiunge voci. Tuttavia, per non sovrascrivere le regole di altri pacchetti, aggiungi un prefisso specifico del pacchetto ai nomi delle regole che esponi.
La callback viene eseguita quando la Factory viene risolta. La closure di verifica della regola viene eseguita più tardi, quando il Validator valida il valore. La registrazione dell’hook non valida alcun input.
Allineare la chiave di destinazione e il controllo di risoluzione
Negli eventi di risoluzione normali del container, vengono selezionate non solo le callback che corrispondono alla chiave registrata, ma anche quelle che corrispondono al tipo dell’oggetto ottenuto. Invece,resolved($name), usato immediatamente da callAfterResolving(), verifica se per la chiave normalizzata rispetto agli alias esiste un flag di risoluzione o un’istanza salvata. Non effettua una ricerca su tutti gli oggetti già creati.
Per questo, il fatto che un’interfaccia o una classe sia già stata risolta non garantisce che resolved() restituisca true anche per un’altra chiave. Verifica il binding e gli alias effettivi del servizio di destinazione e testa anche il caso già risolto con la stessa chiave. Nell’esempio precedente si specifica validator, registrato dal provider ufficiale.
Inoltre, resolved() include anche il caso “risolto in passato”. Non significa che l’istanza corrente sia necessariamente ancora presente.
Non è un’API che garantisce un’esecuzione “una sola volta”
callAfterResolving() lascia la callback registrata. Anche dopo l’esecuzione immediata, la callback registrata verrà eseguita ogni volta che l’oggetto viene risolto di nuovo in futuro. Inoltre, chiamando di nuovo il metodo si aggiunge un’altra callback.
Fai particolare attenzione se un binding normale non condiviso è già stato risolto. Nell’implementazione esaminata accade quanto segue.
- Viene registrata la nuova callback.
- Poiché
resolved()è true, viene chiamatomake(). make()crea un nuovo oggetto e la callback viene eseguita nel relativo evento di risoluzione.- La callback immediata viene eseguita anche sullo stesso oggetto restituito da
make().
Usare un’altra API per sostituire l’oggetto
Il valore restituito dalla callbackafterResolving non viene usato per sostituire l’oggetto restituito dal container. Se vuoi restituire un decorator e sostituire il servizio stesso, valuta l’extend() del container. Il contratto della closure di questa API prevede che restituisca il servizio modificato.
Allo stesso modo, callAfterResolving() non è un meccanismo che aggiorna tutte le dipendenze già salvate in altri oggetti. Se lo scopo è aggiornare le dipendenze al momento di un nuovo binding, consulta rebinding() nella documentazione ufficiale e la progettazione della classe interessata.
Per i requisiti di un provider che carichi davvero i servizi in modo differito, consulta DeferrableProvider. Usare un hook e rendere differito il provider che registra quell’hook sono due cose distinte.
Combinazioni da verificare durante gli aggiornamenti
Prima di rilasciare il pacchetto, verifica non solo il normale ordine di avvio, ma anche il caso in cui un altro provider abbia usato il servizio in precedenza.
Se registri con un flag statico che l’hook è stato usato una sola volta nell’intera applicazione, la configurazione potrebbe mancare nei servizi ricreati in un nuovo container o nei test. Per quanto possibile, garantisci l’idempotenza della configurazione a livello del singolo oggetto di destinazione o della chiave registrata.
Pagine correlate
Sovrascrivere e aggiornare le view di un pacchetto
Un esempio di come loadViewsFrom() usa un hook post-risoluzione per registrare il namespace delle view.
Sovrascrittura e aggiornamento delle traduzioni di un pacchetto
La registrazione del namespace nel Translator e la gestione delle traduzioni già caricate.
Fonti primarie consultate
- Documentazione ufficiale di Laravel: sviluppo di pacchetti
- Documentazione ufficiale di Laravel: Container events, Extending bindings, Rebinding
- Documentazione ufficiale di Laravel: regole di validazione personalizzate
- ServiceProvider: callAfterResolving() e uso nella registrazione delle risorse
- Container: resolved(), resolve(), afterResolving()
- ValidationServiceProvider: registrazione singleton di validator
- Validation Factory: registrazione delle regole con extend()