> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Macro di risposta e contratti HTTP nei pacchetti

> Esamina ResponseFactory e Macroable di Laravel 13 per comprendere le risposte JSON specifiche di un pacchetto, le collisioni di nomi, la durata dello stato di registrazione e come testare e mantenere i contratti HTTP.

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](https://kawax.biz/it/responses.md) e del [trait Macroable](https://kawax.biz/it/advanced/macroable.md), 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.

| Destinazione | Ruolo | Uso in questa pagina |
| - | - | - |
| `Illuminate\Support\Facades\Response` | Facade che risolve e chiama il contratto `ResponseFactory` | Registrazione e chiamata della macro |
| `Illuminate\Routing\ResponseFactory` | API di costruzione come `json()`, `view()` e `noContent()` | Classe che contiene la macro |
| `Illuminate\Http\Response` | Risposta HTTP già generata | Registro di macro separato dalla Factory |
| `Illuminate\Http\JsonResponse` | Risposta JSON | Valore restituito da questa macro |

`response()` senza argomenti restituisce la Factory. Invece `response('corpo')` e `response(null)` restituiscono una risposta creata con `make()`, quindi non chiamano la stessa destinazione.

> [!WARNING]
>
> 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](https://kawax.biz/it/advanced/package-discovery.md).

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.

```php theme={null}
<?php

namespace Acme\Http;

use Illuminate\Http\JsonResponse;
use Illuminate\Routing\ResponseFactory;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\ServiceProvider;
use InvalidArgumentException;

final class HttpServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Response::macro('acmeFailure', function (
            string $code,
            string $message,
            int $status = 422,
            array $headers = [],
        ): JsonResponse {
            if ($status < 400 || $status > 599) {
                throw new InvalidArgumentException(
                    'acmeFailure requires a status between 400 and 599.'
                );
            }

            /** @var ResponseFactory $this */
            return $this->json([
                'error' => [
                    'code' => $code,
                    'message' => $message,
                ],
            ], $status, $headers);
        });
    }
}
```

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](https://kawax.biz/it/advanced/deferred-provider.md).

### 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.

```php theme={null}
return response()->acmeFailure(
    code: 'order_conflict',
    message: '注文を確定できませんでした。',
    status: 409,
    headers: ['X-Acme-Error' => 'order_conflict'],
);
```

Lo stato HTTP è `409` e il JSON ha la forma seguente. Come con un normale `json()`, il `Content-Type` è `application/json`.

```json theme={null}
{
  "error": {
    "code": "order_conflict",
    "message": "注文を確定できませんでした。"
  }
}
```

Puoi chiamare la stessa macro della Factory anche tramite la facade.

```php theme={null}
use Illuminate\Support\Facades\Response;

return Response::acmeFailure(
    'order_conflict', '注文を確定できませんでした。', 409
);
```

> [!NOTE]
>
> 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.

```mermaid theme={null}
flowchart TD
    A["response()->acmeFailure(...)<br>oppure Response::acmeFailure(...)"] --> B["Istanza di ResponseFactory"]
    B --> C["Macroable::__call()<br>Ricollega il $this della Closure"]
    C --> D["ResponseFactory::json()"]
    D --> E["Nuova JsonResponse"]
```

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.

| Situazione | Comportamento nell'implementazione verificata |
| - | - |
| Registrare di nuovo una macro con lo stesso nome | La registrazione successiva sovrascrive la macro precedente |
| Registrare un nome uguale a un metodo pubblico realmente esistente nella classe, come `json()` | Viene chiamato il metodo normale e la macro non viene mai raggiunta |
| `hasMacro($name)` è `true` | Significa che il nome è registrato. Non garantisce l'assenza di collisioni con un metodo reale |
| `mixin($object, replace: false)` | Evita di sostituire le macro già registrate. Non controlla le collisioni con i metodi reali |

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](https://kawax.biz/it/advanced/package-service-lifetimes.md) tratta più in dettaglio lo stato condiviso del container e lo stato per singola richiesta.

> [!WARNING]
>
> `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](https://kawax.biz/it/http-tests.md). Per preparare un ambiente di pacchetto autonomo, consulta [Testare i pacchetti](https://kawax.biz/it/advanced/package-testing.md).

```php theme={null}
<?php

namespace Tests\Feature;

use Acme\Http\HttpServiceProvider;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Facades\Route;
use InvalidArgumentException;
use Tests\TestCase;

class ResponseMacroTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        $this->app->register(HttpServiceProvider::class);
    }

    public function test_http_response_keeps_the_public_contract(): void
    {
        Route::get('/_acme/failure', function (): JsonResponse {
            return response()->acmeFailure(
                'order_conflict',
                '注文を確定できませんでした。',
                409,
                ['X-Acme-Error' => 'order_conflict'],
            );
        });

        $this->getJson('/_acme/failure')
            ->assertStatus(409)
            ->assertHeader('Content-Type', 'application/json')
            ->assertHeader('X-Acme-Error', 'order_conflict')
            ->assertExactJson([
                'error' => [
                    'code' => 'order_conflict',
                    'message' => '注文を確定できませんでした。',
                ],
            ]);
    }

    public function test_facade_returns_a_fresh_json_response(): void
    {
        $first = Response::acmeFailure('first', '最初のエラー');
        $second = response()->acmeFailure('second', '次のエラー', 503);

        $this->assertInstanceOf(JsonResponse::class, $first);
        $this->assertNotSame($first, $second);
        $this->assertSame(422, $first->getStatusCode());

        $first->setData(['changed' => true]);

        $this->assertSame(503, $second->getStatusCode());
        $this->assertSame('second', $second->getData(true)['error']['code']);
    }

    public function test_success_status_is_rejected(): void
    {
        $this->expectException(InvalidArgumentException::class);

        response()->acmeFailure('invalid_status', '設定を確認してください。', 200);
    }
}
```

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](https://kawax.biz/it/eloquent-resources.md) 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

* [Documentazione ufficiale di Laravel: Response macros e JSON responses (13.x)](https://github.com/laravel/docs/blob/13.x/responses.md#response-macros)
* [Documentazione ufficiale di Laravel: Package development (13.x)](https://github.com/laravel/docs/blob/13.x/packages.md)
* [La facade Response e la sua destinazione di risoluzione (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Support/Facades/Response.php)
* [Inoltro della Facade all'istanza (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Support/Facades/Facade.php)
* [L'helper response() (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Foundation/helpers.php)
* [Logica di costruzione di ResponseFactory (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Routing/ResponseFactory.php)
* [Registrazione, collegamento e reset in Macroable (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Macroable/Traits/Macroable.php)
* [Test ufficiali di Macroable (v13.40.0)](https://github.com/laravel/framework/blob/v13.40.0/tests/Support/SupportMacroableTest.php)


## Related topics

- [Trait Macroable](https://kawax.biz/it/advanced/macroable.md)
- [Argomenti avanzati](https://kawax.biz/it/advanced/index.md)
- [Store di cache personalizzati e contratti nei pacchetti](https://kawax.biz/it/advanced/package-cache-drivers.md)
- [Client HTTP e test nei pacchetti](https://kawax.biz/it/advanced/package-http-client.md)
- [Driver di sessione personalizzati nei pacchetti](https://kawax.biz/it/advanced/package-session-drivers.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.