Vai al contenuto principale
A volte vuoi che gli utenti del tuo pacchetto possano restituire lo stesso formato di JSON di errore da molti controller. Pubblicando una macro di risposta, puoi aggiungere un’API di costruzione come response()->acmeFailure(...) senza richiedere che i controller estendano una classe base. Questa pagina si basa sulla documentazione ufficiale di Laravel nel branch predefinito 13.x e sull’implementazione ufficiale dell’ultima release del Framework, v13.40.0, verificate l’8 novembre 2026. Partendo dalle basi di Risposte e del trait Macroable, tratta i confini da tenere presenti quando mantieni una macro come API pubblica di un pacchetto distribuito.

Si registra sulla Factory, non sulla risposta

Il nome Response indica sia una facade che richiede la costruzione di una risposta sia la classe della risposta HTTP generata. Ciò che Response::macro() della documentazione ufficiale estende è ResponseFactory, la classe in cui viene risolta la facade. response() senza argomenti restituisce la Factory. Invece response('corpo') e response(null) restituiscono una risposta creata con make(), quindi non chiamano la stessa destinazione.
Registrare su Illuminate\Http\Response::macro() non crea una macro per response()->acmeFailure(). Non confondere nelle istruzioni use la facade con la classe di risposta omonima.

Pubblicare un’API di costruzione per il JSON di errore

Registrarla in un service provider

Registra una macro con il prefisso del pacchetto nel metodo boot() di src/HttpServiceProvider.php. Per sapere come viene caricato il provider, consulta Discovery dei pacchetti. Il contratto pubblico di questo esempio è un JSON con error.code ed error.message, insieme a uno stato HTTP compreso tra 400 e 599. Questa restrizione sullo stato non è un limite del meccanismo delle macro di Laravel. È il pacchetto a definirla per un’API dedicata agli errori.
Questo provider deve avviarsi prima che la macro venga usata. Non mettere la registrazione in un file che carica le route HTTP, né all’interno di una condizione runningInConsole() negata. Anche i test di funzionalità HTTP vengono eseguiti dalla CLI. Un provider che si occupa solo di questa registrazione non deve nemmeno essere un provider differito in attesa della risoluzione di un servizio. Per il funzionamento della registrazione differita, consulta Service provider differiti.

Chiamarla da un controller

Dopo che la logica di business ha rilevato un conflitto, passa il codice di errore scelto e un messaggio per l’utente.
Lo stato HTTP è 409 e il JSON ha la forma seguente. Come con un normale json(), il Content-Type è application/json.
Puoi chiamare la stessa macro della Factory anche tramite la facade.
La macro costruisce solo le risposte nei punti in cui la chiami. Non converte automaticamente le eccezioni di validazione o gli errori di autenticazione in questo formato. Non aggiunge neanche alcun passaggio tra HTML e JSON in base all’header Accept.

Il $this della Closure e il percorso di chiamata

La Factory usa Macroable. Quando chiami un metodo non definito su un’istanza, __call() cerca la macro registrata e ricollega una normale Closure all’istanza su cui è stata chiamata. Anche se il codice di registrazione si trova in un metodo del service provider, $this a runtime è la Factory e non il service provider. Per questo $this->json() nell’esempio chiama il normale metodo della Factory.
  • Definisci le macro che usano $this con una normale function (...) { ... }. Una static function non ha il $this di istanza.
  • Se registri un oggetto invocabile, non viene ricollegato come una Closure. Fai in modo che sia l’oggetto stesso a ricevere le dipendenze necessarie, ad esempio tramite il proprio costruttore.
  • Gli argomenti della macro sono quelli passati dal chiamante. Anche se dichiari il tipo Request o simili, Macroable non lo inietta automaticamente dal container.
La chiamata tramite facade Response::acmeFailure() sembra statica, ma internamente viene inoltrata all’istanza della Factory. Invece Illuminate\Routing\ResponseFactory::acmeFailure() sulla classe concreta è una vera chiamata statica, e __callStatic() collega la Closure senza istanza. Una macro come questa, che usa $this->json(), non supporta le chiamate statiche sulla classe concreta.

Trattare le collisioni di nomi come un problema di compatibilità

macro() si limita ad assegnare un valore in un array statico indicizzato per nome. Registrare un nome esistente non genera alcun avviso. Scegli un nome che identifichi il tuo pacchetto, come acmeFailure, invece di un nome generico come failure. Quando aggiorni Laravel, controlla anche le collisioni con i metodi reali usando method_exists(ResponseFactory::class, 'acmeFailure'). Se controlli hasMacro() e salti la registrazione senza condizioni, rischi di usare l’implementazione di un altro pacchetto come se fosse la tua API. Solo quando rendi “l’applicazione può sovrascriverla registrandola prima” parte della specifica, pubblica tale priorità insieme agli argomenti e al valore restituito richiesti. L’esempio sopra segue la politica di registrare la definizione del pacchetto ogni volta che il provider si avvia.

Separare lo stato di registrazione dalla durata delle risposte

Il registro delle macro è una proprietà statica della Factory. Ricreare la Factory o cancellare l’istanza risolta della facade non svuota questo registro. Lo stato di registrazione può persistere anche nei test che ricreano l’applicazione all’interno dello stesso processo PHP. D’altra parte, la macro dell’esempio fa creare a json() una nuova JsonResponse a ogni chiamata. Ciò che viene condiviso è la logica di costruzione, non il corpo o gli header della risposta. Questi sono i confini da rispettare nei worker di lunga durata.
  1. Non catturare la Request corrente, l’utente autenticato o il tenant con use (...) al momento della registrazione. Passa questi valori come argomenti al momento della chiamata.
  2. Non riutilizzare una risposta creata al momento della registrazione. Restituisci ogni volta una nuova risposta.
  3. Se catturi la configurazione come valore al momento della registrazione, includi nella specifica che quel valore viene mantenuto nel processo.
  4. Dopo aver aggiornato il pacchetto, riavvia i processi di lunga durata affinché il nuovo codice di registrazione abbia effetto.
Progettare la durata dei servizi dei pacchetti tratta più in dettaglio lo stato condiviso del container e lo stato per singola richiesta.
Response::flushMacros() elimina tutte le macro registrate sulla Factory. Vanno perse anche le registrazioni degli altri pacchetti, quindi non usarlo nella gestione delle richieste in produzione né come pulizia frettolosa in un ambiente di test condiviso. Verifica collisioni e reset in un processo di test isolato.

Testare il contratto HTTP con valori restituiti reali

In un’applicazione di test Laravel 13 in grado di caricare il pacchetto tramite Composer, aggiungi il seguente tests/Feature/ResponseMacroTest.php. Per Tests\TestCase e le basi dei test HTTP, consulta Test HTTP. Per preparare un ambiente di pacchetto autonomo, consulta Testare i pacchetti.
Se simuli l’API di costruzione stessa, come con Response::shouldReceive('acmeFailure'), la registrazione della macro, il collegamento della Closure e la generazione del JSON non vengono eseguiti. Tieni separata la verifica del numero di chiamate e controlla la vera risposta HTTP come mostrato sopra. Inoltre, considera il corpo, lo stato e gli header ricevuti dagli utenti come oggetto di compatibilità.
  • Mantieni error.code stabile per i controlli automatici e distinguilo dalle modifiche al messaggio visualizzato.
  • I nomi degli argomenti vengono usati come argomenti con nome, quindi verifica l’impatto di una loro modifica come per i valori predefiniti o l’ordine.
  • Restituisci le risposte senza corpo, come 204, con un’altra API come noContent(), non come JSON di errore.
  • Se consenti estensioni che modificano il Content-Type tramite header personalizzati, mantienilo coerente con il formato del corpo.
  • Se il tuo obiettivo è la serializzazione dei dati o la paginazione, valuta anche le risorse API di Eloquent invece di reimplementarle solo con le macro.

Checklist per la manutenzione a lungo termine

  • Nelle combinazioni Laravel 13 supportate, il provider si avvia e la macro può essere chiamata sia dall’helper sia dalla facade.
  • Il nome della macro non entra in collisione con altre registrazioni o con metodi reali della Factory.
  • La compatibilità di chiavi JSON, stato HTTP, header, valori predefiniti e argomenti con nome è coperta da test.
  • Le chiamate consecutive non condividono risposte e al momento della registrazione non vengono conservate informazioni su Request o utente.
  • I test che reimpostano le registrazioni sono isolati dagli altri pacchetti.
  • Il riavvio dei processi di lunga durata fa parte della procedura di aggiornamento.

Fonti primarie consultate

Ultima modifica il 8 novembre 2026