Framework/src/Http/Responses/JsonApiResponseFactory.php

64 lines
1.9 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Phred\Http\Responses;
use Nyholm\Psr7\Factory\Psr17Factory;
use Phred\Http\Contracts\ApiResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
final class JsonApiResponseFactory implements ApiResponseFactoryInterface
{
public function __construct(private Psr17Factory $psr17 = new Psr17Factory()) {}
public function ok(array $data = []): ResponseInterface
{
return $this->document(['data' => $data], 200);
}
public function created(array $data = [], ?string $location = null): ResponseInterface
{
$res = $this->document(['data' => $data], 201);
if ($location) {
$res = $res->withHeader('Location', $location);
}
return $res;
}
public function noContent(): ResponseInterface
{
// JSON:API allows 204 without body
return $this->psr17->createResponse(204);
}
public function error(int $status, string $title, ?string $detail = null, array $extra = []): ResponseInterface
{
$error = array_filter([
'status' => (string) $status,
'title' => $title,
'detail' => $detail,
], static fn($v) => $v !== null && $v !== '');
if (!empty($extra)) {
$error = array_merge($error, $extra);
}
return $this->document(['errors' => [$error]], $status);
}
public function fromArray(array $payload, int $status = 200): ResponseInterface
{
// Caller must ensure payload is a valid JSON:API document shape
return $this->document($payload, $status);
}
/**
* @param array<string,mixed> $doc
*/
private function document(array $doc, int $status): ResponseInterface
{
$res = $this->psr17->createResponse($status)
->withHeader('Content-Type', 'application/vnd.api+json');
$res->getBody()->write(json_encode($doc, JSON_UNESCAPED_SLASHES));
return $res;
}
}