0

Response macros — adding custom response methods globally

Advanced5 min read·lv-10-005
laravel-src

Concept

Response macros and custom response classes allow extending Laravel's response system to enforce consistent API response formatting across an application.

Response::macro(string $name, callable $macro): Adds a method to all Response instances. Defined in AppServiceProvider::boot(). Available on both the response() helper and Response facade.

Responsable interface: Any class implementing Illuminate\Contracts\Support\Responsable can be returned directly from controllers. Laravel calls toResponse(Request $request) on it and uses the result as the HTTP response. This enables rich response objects that carry both data and HTTP semantics.

Custom JSON Resource responses: API Resources (covered in lv-24) extend JsonResource and implement Responsable. Returning new UserResource($user) from a controller automatically returns a JSON response.

Standard API response pattern: Many teams define a consistent envelope: {'success': bool, 'data': mixed, 'message': string}. Implement via a macro or a ApiResponse class.

Response tapping: ->withCallback(callable $callback) (JSONP), ->setCallback(). Less commonly used.

Streaming responses: response()->stream(callable $callback, int $status, array $headers) — sends chunked transfer encoding for server-sent events or streaming data.

Code Example

php
<?php
// AppServiceProvider::boot()
use Illuminate\Support\Facades\Response;

// API response macros
Response::macro('success', function(mixed $data = null, string $message = 'Success', int $status = 200) {
    return response()->json([
        'success' => true,
        'message' => $message,
        'data'    => $data,
    ], $status);
});

Response::macro('error', function(string $message, int $status = 400, array $errors = []) {
    return response()->json(array_filter([
        'success' => false,
        'message' => $message,
        'errors'  => $errors ?: null,
    ]), $status);
});

Response::macro('paginated', function(\Illuminate\Pagination\LengthAwarePaginator $paginator) {
    return response()->json([
        'success' => true,
        'data'    => $paginator->items(),
        'meta'    => [
            'total'        => $paginator->total(),
            'per_page'     => $paginator->perPage(),
            'current_page' => $paginator->currentPage(),
            'last_page'    => $paginator->lastPage(),
        ],
    ]);
});

// Usage in controllers
return response()->success($user, 'User created', 201);
return response()->error('Validation failed', 422, $errors);
return response()->paginated(User::paginate(15));

// Responsable interface — return custom objects from controllers
class OrderSummaryResponse implements \Illuminate\Contracts\Support\Responsable
{
    public function __construct(private readonly Order $order) {}

    public function toResponse($request): \Illuminate\Http\Response
    {
        return response()->json([
            'order'    => new OrderResource($this->order),
            'totals'   => $this->order->calculateTotals(),
            'timeline' => $this->order->getTimeline(),
        ]);
    }
}

// Controller returns the Responsable object directly
public function show(Order $order): OrderSummaryResponse
{
    return new OrderSummaryResponse($order);
}