In Laravel, how do Response::macro() and a class implementing Responsable each let you reuse one response shape across controllers?
answer
- extend the factory or teach an object
- register macros in AppServiceProvider::boot
- response()->yourMacro(...) after registration
- toResponse($request) returns a response
- router calls toResponse() before other rules
basics
~10 sResponse::macro('name', fn) adds a method to the response factory, so response()->name(...) builds the shared shape anywhere. A Responsable class implements toResponse($request), and the router calls it when a controller returns that object.
solid answer
~40 sThe `Response` facade points at the `ResponseFactory` contract, and `Illuminate\Routing\ResponseFactory` uses the `Macroable` trait. Calling `Response::macro('ticketPdf', function ($ticket) { ... })` in `AppServiceProvider::boot()` makes `response()->ticketPdf($ticket)` available everywhere; inside the closure `$this` is the factory, so it can call `$this->json()` or `$this->download()`. A macro suits a small reusable call shape. `Illuminate\Contracts\Support\Responsable` suits a response with its own data and logic: the class implements `toResponse($request)`, the controller returns an instance, and `Router::toResponse()` calls that method before any other conversion. Responsable objects are easy to unit test and can vary by request, for example sending JSON to an API client and HTML to a browser.
code
php · 20 lines<?php
namespace App\Http\Responses;
use App\Models\Ticket;
use Illuminate\Contracts\Support\Responsable;
class TicketResponse implements Responsable
{
public function __construct(private Ticket $ticket) {}
public function toResponse($request)
{
if ($request->expectsJson()) {
return response()->json(['id' => $this->ticket->id, 'seat' => $this->ticket->seat]);
}
return response()->file(storage_path('app/private/'.$this->ticket->pdf_path));
}
}go deeper
Recall that Response::macro() registers a reusable response() method in a service provider, and that returning a Responsable object lets it build its own response.
Explain that the facade targets the macroable ResponseFactory, that closures are bound to the factory, and that Router::toResponse() checks Responsable first.
Choose macros for thin envelopes and Responsable classes for logic that varies by request, register in boot(), and watch for global macro name clashes.
Decide where response conventions live so teams share one envelope, and keep them testable rather than scattered across closures.
## The problem both solve In a concert-venue app, several controllers return the same kind of reply: a JSON status block for the door scanners (`{"ok": true, "gate": "B", "at": ...}`), or a PDF ticket that is shown inline to browsers but returned as JSON metadata to the mobile app. Copying the same `response()->json([...], 200, [...])` into each controller drifts over time. Laravel gives you two reuse points. ## Response macros: add a method to the factory `Illuminate\Support\Facades\Response` resolves the `Illuminate\Contracts\Routing\ResponseFactory` contract, whose implementation `Illuminate\Routing\ResponseFactory` uses `Macroable`. Registering a macro adds a named method: ```php use Illuminate\Support\Facades\Response; Response::macro('gateStatus', function (string $gate, bool $ok = true) { return $this->json(['ok' => $ok, 'gate' => $gate, 'at' => now()->toIso8601String()]); }); ``` Key points: - **Where:** in the `boot()` method of a service provider, typically `App\Providers\AppServiceProvider`, so the macro exists before any route runs. - **How it is called:** `response()->gateStatus('B')`, or `Response::gateStatus('B')` through the facade. - **What `$this` is:** `Macroable::__call()` binds the closure to the factory instance, so `$this->json()`, `$this->view()` and `$this->download()` are available. - **Unknown names fail loudly:** calling a macro that was never registered throws `BadMethodCallException` ("Method ... does not exist."). - **Other classes are macroable too:** `Illuminate\Http\Response`, `JsonResponse` and `RedirectResponse` use `Macroable` as well, so a macro registered on one of those classes becomes a chainable method on its instances rather than a factory method. Macros are best for thin wrappers that add a status, headers or a standard envelope. ## Responsable: let an object build its own response `Illuminate\Contracts\Support\Responsable` has one method, `toResponse($request)`, which returns a Symfony response. When a controller returns such an object, `Router::toResponse()` calls it **first**, before the array, model and string rules. That gives you a class with a constructor, dependencies, private helpers and tests: 1. The controller gathers data and returns `new TicketResponse($ticket)`. 2. The router calls `$ticketResponse->toResponse($request)`. 3. The object decides, for instance with `$request->expectsJson()`, whether to return `response()->json(...)` or `response()->file(...)`. API resource classes are the best-known example of this interface; their own features (wrapping, conditional attributes) are a separate subject. ## Choosing between them | | `Response::macro()` | `Responsable` class | |---|---|---| | Unit of reuse | a named factory method | a class with its own data | | Registered | in a provider's `boot()` | nowhere; return an instance | | Holds logic and dependencies | a closure only | constructor, methods, tests | | Varies by request | possible, but awkward | natural, `toResponse()` receives the request | | Static analysis and IDE support | needs docblocks or helper files | a normal class | ## Pitfalls - A macro registered in `register()` or inside a controller can be missing when another code path runs first. Register in `boot()`. - Macros are global names; two packages registering the same name overwrite each other, since the last registration wins. - A `Responsable` that returns a plain array from `toResponse()` breaks the contract; it must return a response object. ## Seeing both in the venue app 1. The door-scanner endpoints all end with `return response()->gateStatus($gate->code);`, so a change to the envelope (say, adding a `version` field) is one edit in `AppServiceProvider`. 2. The ticket endpoints end with `return new TicketResponse($ticket);`. The class checks whether the client wants JSON, and returns either metadata or the PDF through `response()->file()`. 3. Each `TicketResponse` can be tested by constructing it with a ticket and a fake request and asserting on the response it returns, without routing a full HTTP request. The two approaches also combine: a `Responsable` class can call a macro inside `toResponse()` to reuse the shared envelope. ## What interviewers look for Knowing that the facade targets the factory contract, where to register a macro, and that `Responsable` is the hook the router checks first. Choosing the class over the macro once the shape has logic shows design sense.
- Inside a `Response::macro()` closure, why can you call `$this->json()`?`Macroable::__call()` binds the stored closure to the object the macro is called on. For `response()->gateStatus()` that object is the `ResponseFactory`, so `$this` refers to it and all factory methods such as `json()`, `view()` and `download()` are available.
- What is the difference between `Response::macro()` and `JsonResponse::macro()`?The facade version adds a method to the response factory, used as `response()->name()`. `JsonResponse::macro()` adds a method to JSON response instances, so you chain it after building one, as in `response()->json($data)->name()`. One creates responses, the other decorates an existing one.
saying these in an interview costs you the question
- Response::macro() must be called inside each controller that uses it.
- A Responsable object is converted to JSON like any other object.
- Inside a macro closure, $this is the service provider that registered it.
- A missing macro name silently returns an empty response.