Zum Hauptinhalt springen
Manchmal möchten Sie, dass die Nutzer Ihres Pakets aus vielen Controllern dasselbe Fehler-JSON-Format zurückgeben können. Wenn Sie ein Response-Macro veröffentlichen, können Sie eine Erzeugungs-API wie response()->acmeFailure(...) hinzufügen, ohne dass Controller von einer Basisklasse erben müssen. Diese Seite basiert auf der offiziellen Laravel-Dokumentation im Standard-Branch 13.x und der offiziellen Implementierung des neuesten Framework-Releases v13.40.0, geprüft am 8. November 2026. Aufbauend auf den Grundlagen von Responses und dem Macroable-Trait behandelt sie die Grenzen, die Sie beachten sollten, wenn Sie ein Macro als öffentliche API eines verteilten Pakets pflegen.

Registrierung an der Factory, nicht an der Response

Der Name Response bezeichnet sowohl eine Facade, die die Erzeugung einer Antwort anfordert, als auch die Klasse der erzeugten HTTP-Antwort. Response::macro() aus der offiziellen Dokumentation erweitert ResponseFactory, also die Klasse, in die die Facade aufgelöst wird. response() ohne Argumente gibt die Factory zurück. response('Inhalt') und response(null) geben dagegen eine mit make() erzeugte Antwort zurück und rufen daher nicht dasselbe Ziel auf.
Eine Registrierung über Illuminate\Http\Response::macro() erzeugt kein Macro für response()->acmeFailure(). Verwechseln Sie in Ihren use-Anweisungen nicht die Facade mit der gleichnamigen Response-Klasse.

Eine Erzeugungs-API für Fehler-JSON veröffentlichen

Im Service Provider registrieren

Registrieren Sie in der Methode boot() von src/HttpServiceProvider.php ein Macro mit Paketpräfix. Wie der Provider geladen wird, erfahren Sie unter Paket-Discovery. Der öffentliche Vertrag in diesem Beispiel besteht aus JSON mit error.code und error.message sowie einem HTTP-Status zwischen 400 und 599. Diese Statusbeschränkung ist keine Einschränkung des Macro-Mechanismus von Laravel. Das Paket legt sie für eine reine Fehler-API fest.
Dieser Provider muss gestartet sein, bevor das Macro verwendet wird. Platzieren Sie die Registrierung nicht in einer Datei, die HTTP-Routen lädt, und nicht innerhalb einer negierten runningInConsole()-Bedingung. Auch HTTP-Feature-Tests laufen über die CLI. Ein Provider, der nur diese Registrierung übernimmt, muss auch kein verzögerter Provider sein, der auf die Auflösung eines Dienstes wartet. Wie verzögerte Registrierung funktioniert, erfahren Sie unter Verzögerte Service Provider.

Aus einem Controller aufrufen

Nachdem Ihre Geschäftslogik einen Konflikt erkannt hat, übergeben Sie den festgelegten Fehlercode und eine Meldung für den Benutzer.
Der HTTP-Status ist 409, und das JSON sieht wie folgt aus. Wie beim normalen json() ist der Content-Type application/json.
Auch über die Facade können Sie dasselbe Factory-Macro aufrufen.
Das Macro erzeugt nur die Antworten, bei denen Sie es aufrufen. Es wandelt Validierungsausnahmen oder Authentifizierungsfehler nicht automatisch in dieses Format um. Es fügt auch keine Umschaltung zwischen HTML und JSON abhängig vom Accept-Header hinzu.

$this in der Closure und der Aufrufpfad

Die Factory verwendet Macroable. Wenn Sie eine nicht definierte Methode einer Instanz aufrufen, sucht __call() das registrierte Macro und bindet eine normale Closure neu an die aufrufende Instanz. Auch wenn der Registrierungscode in einer Methode des Service Providers steht, ist $this zur Laufzeit die Factory und nicht der Service Provider. Deshalb ruft $this->json() im Beispiel die normale Methode der Factory auf.
  • Definieren Sie Macros, die $this verwenden, mit einer normalen function (...) { ... }. Eine static function hat kein Instanz-$this.
  • Wenn Sie ein invokable Objekt registrieren, wird es nicht wie eine Closure neu gebunden. Benötigte Abhängigkeiten erhält dieses Objekt selbst, zum Beispiel über seinen Konstruktor.
  • Die Argumente des Macros sind die Argumente, die der Aufrufer übergibt. Auch wenn Sie Request oder Ähnliches als Typ angeben, injiziert Macroable es nicht automatisch aus dem Container.
Der Facade-Aufruf Response::acmeFailure() sieht statisch aus, wird intern aber an die Factory-Instanz weitergeleitet. Illuminate\Routing\ResponseFactory::acmeFailure() auf der konkreten Klasse ist dagegen ein echter statischer Aufruf, und __callStatic() bindet die Closure ohne Instanz. Ein Macro wie dieses, das $this->json() verwendet, unterstützt keine statischen Aufrufe auf der konkreten Klasse.

Namenskollisionen als Kompatibilitätsproblem behandeln

macro() weist lediglich einem statischen Array mit dem Namen als Schlüssel einen Wert zu. Die Registrierung eines vorhandenen Namens erzeugt keine Warnung. Wählen Sie statt eines allgemeinen Namens wie failure einen Namen, der Ihr Paket erkennen lässt, etwa acmeFailure. Prüfen Sie bei Laravel-Updates zusätzlich mit method_exists(ResponseFactory::class, 'acmeFailure') auf Kollisionen mit echten Methoden. Wenn Sie hasMacro() prüfen und die Registrierung bedingungslos überspringen, verwenden Sie womöglich die Implementierung eines anderen Pakets, als wäre sie Ihre eigene API. Nur wenn Sie „die Anwendung kann es durch vorherige Registrierung überschreiben“ zur Spezifikation machen, sollten Sie diese Rangfolge zusammen mit den erforderlichen Argumenten und dem Rückgabewert veröffentlichen. Das obige Beispiel folgt dem Grundsatz, bei jedem Start des Providers die eigene Definition des Pakets zu registrieren.

Registrierungszustand und Lebensdauer der Antwort trennen

Das Macro-Register ist eine statische Eigenschaft der Factory. Das Neuerzeugen der Factory oder das Löschen der aufgelösten Facade-Instanz leert dieses Register nicht. Auch in Tests, die die Anwendung innerhalb desselben PHP-Prozesses neu erzeugen, kann der Registrierungszustand bestehen bleiben. Das Beispiel-Macro lässt dagegen bei jedem Aufruf json() eine neue JsonResponse erzeugen. Geteilt wird die Erzeugungslogik, nicht der Inhalt oder die Header der Antwort selbst. Diese Grenzen sollten Sie in langlebigen Workern einhalten.
  1. Erfassen Sie bei der Registrierung nicht den aktuellen Request, den authentifizierten Benutzer oder den Mandanten mit use (...). Übergeben Sie diese Werte beim Aufruf als Argumente.
  2. Verwenden Sie keine bei der Registrierung erzeugte Antwort wieder. Geben Sie jedes Mal eine neue Antwort zurück.
  3. Wenn Sie Konfiguration bei der Registrierung als Wert erfassen, nehmen Sie in die Spezifikation auf, dass dieser Wert im Prozess erhalten bleibt.
  4. Starten Sie nach einem Paket-Update langlebige Prozesse neu, damit der neue Registrierungscode wirksam wird.
Lebensdauer von Paketdiensten entwerfen behandelt geteilten Container-Zustand und Zustand pro Request ausführlicher.
Response::flushMacros() löscht alle Macros, die an der Factory registriert sind. Auch Registrierungen anderer Pakete gehen verloren. Verwenden Sie es daher nicht in der Request-Verarbeitung in Produktion oder als unbedachten Aufräumschritt in einer gemeinsam genutzten Testumgebung. Prüfen Sie Kollisionen und Zurücksetzungen in einem isolierten Testprozess.

Den HTTP-Vertrag mit echten Rückgabewerten testen

Legen Sie in einer Laravel-13-Testanwendung, die das Paket über Composer laden kann, die folgende Datei tests/Feature/ResponseMacroTest.php an. Zu Tests\TestCase und den Grundlagen von HTTP-Tests siehe HTTP-Tests, zur Einrichtung einer eigenständigen Paketumgebung siehe Pakete testen.
Wenn Sie die Erzeugungs-API selbst mocken, etwa mit Response::shouldReceive('acmeFailure'), werden Macro-Registrierung, Closure-Bindung und JSON-Erzeugung nicht ausgeführt. Halten Sie Prüfungen der Aufrufanzahl getrennt und prüfen Sie wie oben die echte HTTP-Antwort. Behandeln Sie außerdem Inhalt, Status und Header, die Nutzer erhalten, als Teil der Kompatibilität.
  • Halten Sie error.code für die maschinelle Auswertung stabil und unterscheiden Sie es von Änderungen der Anzeigemeldung.
  • Argumentnamen werden als benannte Argumente verwendet. Prüfen Sie die Auswirkungen ihrer Änderung daher genauso wie bei Standardwerten oder Reihenfolge.
  • Geben Sie Antworten ohne Inhalt wie 204 nicht als Fehler-JSON zurück, sondern über eine andere API wie noContent().
  • Wenn Sie Erweiterungen zulassen, die den Content-Type über eigene Header ändern, halten Sie ihn mit dem Inhaltsformat konsistent.
  • Wenn es um Datenserialisierung oder Paginierung geht, implementieren Sie diese nicht allein mit Macros neu, sondern ziehen Sie auch Eloquent-API-Ressourcen in Betracht.

Checkliste für die langfristige Pflege

  • In den unterstützten Laravel-13-Kombinationen startet der Provider, und das Macro ist sowohl über den Helper als auch über die Facade aufrufbar.
  • Der Macro-Name kollidiert weder mit anderen Registrierungen noch mit echten Methoden der Factory.
  • Die Kompatibilität von JSON-Schlüsseln, HTTP-Status, Headern, Standardwerten und benannten Argumenten ist getestet.
  • Aufeinanderfolgende Aufrufe teilen keine Antworten, und bei der Registrierung werden keine Request- oder Benutzerinformationen gespeichert.
  • Tests, die Registrierungen zurücksetzen, sind von anderen Paketen isoliert.
  • Der Neustart langlebiger Prozesse ist Teil des Update-Verfahrens.

Verwendete Primärquellen

Zuletzt geändert am 8. November 2026