ADR - Responders¶
Overview¶
The responder is the only element in ADR that speaks HTTP. It takes the domain's payload and builds the response: status code, headers, and body. Keeping all of this in one place is what allows the action and the domain to remain free of presentation concerns.
Every responder implements Phalcon\Contracts\ADR\Responder\Responder, a single invokable method that receives the request, a response to build upon, and the payload, and returns the finished response:
public function __invoke(
Phalcon\Http\RequestInterface $request,
Phalcon\Http\ResponseInterface $response,
Phalcon\Contracts\ADR\Payload\Payload $payload
): Phalcon\Http\ResponseInterface;
Built-in responders¶
Two ready-to-use responders cover the common content types:
Phalcon\ADR\Responder\JsonResponder- serializes the payload result as JSON.Phalcon\ADR\Responder\TextResponder- renders the payload result as plain text.
Both take no constructor arguments and can be injected into an action directly, or bound as the default Responder in the container:
use Phalcon\ADR\Responder\JsonResponder;
$response = (new JsonResponder())($request, new Response(), $payload);
The responder chain¶
JsonResponder and TextResponder are thin subclasses of Phalcon\ADR\Responder\AbstractFormattedResponder, which is itself a ChainResponder. Building a response is broken into a chain of small, single-purpose responders that each contribute one part of the response:
StatusResponder- sets the HTTP status code from the payload's status.RedirectResponder- turns a redirect into aLocationheader and status.FormatResponder- negotiates a representation and writes the body.
A subclass supplies the formatter(s); the chain does the rest. You can compose your own chain with ChainResponder when you need a different combination.
Status mapping¶
StatusResponder translates the domain's status vocabulary into an HTTP status code using Phalcon\ADR\Responder\StatusMapper:
The mapper ships with a default for every Status::* constant. Pass overrides to the constructor to change or extend the mapping:
Any status the mapper does not recognize falls back to 500, never a silent 200.
Content negotiation¶
FormatResponder chooses a representation based on the request's Accept header. Each representation is a Phalcon\Contracts\ADR\Responder\Formatter\Formatter:
interface Formatter
{
public function accepts(string $acceptHeader): bool;
public function contentType(): string;
public function format(Phalcon\Contracts\ADR\Payload\Payload $payload): string;
}
JsonFormatter and TextFormatter are provided. If no formatter accepts the header, FormatResponder falls back to the configured default rather than leaving the response empty.
Redirects¶
A domain never speaks HTTP, so a redirect is expressed as a small value object, Phalcon\ADR\Responder\Redirect, carried on the payload result:
use Phalcon\ADR\Responder\Redirect;
return Payload::found(new Redirect('/invoices/42')); // 302 by default
return Payload::found(new Redirect('/invoices/42', 301)); // permanent
RedirectResponder reacts to a Redirect result by setting the status code and the Location header; for any other result it does nothing.
Writing a responder¶
Implement Responder to build a response any way you like:
<?php
namespace MyApp\Responder;
use Phalcon\Contracts\ADR\Payload\Payload;
use Phalcon\Contracts\ADR\Responder\Responder;
use Phalcon\Http\RequestInterface;
use Phalcon\Http\ResponseInterface;
final class CsvResponder implements Responder
{
public function __invoke(
RequestInterface $request,
ResponseInterface $response,
Payload $payload
): ResponseInterface {
return $response
->setContentType('text/csv')
->setContent($this->toCsv($payload->getResult()));
}
}