Відповіді
Кожен action контролера повертає Psr\Http\Message\ResponseInterface (PSR-7).
Після Router::dispatch() ядро викликає SapiEmitter (Laminas) —
заголовки та тіло потрапляють у PHP SAPI (FPM, Apache, built-in server).
Для доступу до поточного запиту в будь-якому місці програми можна використовувати сервіс RequestProxy.
Див. також: контролери, життєвий цикл → відповідь, Views (Twig).
Дві фабрики
| Інтерфейс | Клас | Призначення |
|---|---|---|
ResponseFactoryInterface |
Concept\Core\Http\ResponseFactory |
JSON, redirect, порожня відповідь, back() |
ViewResponseFactoryInterface |
Concept\Core\Services\View\ViewResponseFactory |
HTML через Twig |
Обидві реєструються в HttpServiceProvider. У контролері — через конструктор (автовайринг):
public function __construct(
private readonly ResponseFactoryInterface $response,
private readonly ViewResponseFactoryInterface $viewResponse,
) {}
HTML (Twig)
return $this->viewResponse->create('@cabinet/index', [
'user' => $user,
'projects' => $projects,
]);
@cabinet/index— логічне ім’я шаблону (префікс компонента)- Другий аргумент — локальні змінні для цього action
- Третій аргумент — HTTP-код (за замовчуванням
200)
ViewResponseFactory зливає дані з атрибута запиту
Concept\Core\Http\Requests\RequestAttribute::VIEW_PAYLOAD (flash, errors, old, CSRF
з ShareViewDataMiddleware), потім рендерить Twig
і виставляє Content-Type: text/html.
Детальніше про шаблони — Представлення.
JSON
Довільний JSON — json()
return $this->response->json([
'token' => bin2hex(random_bytes(32)),
]);
Тіло — ваш масив як є; статус за замовчуванням 200.
Узгоджений успіх — jsonSuccess()
return $this->response->jsonSuccess(['id' => $user->getId()]);
// Відповідь (приклад)
// { "status": "success", "code": 200, "data": { "id": 42 } }
Узгоджена помилка — jsonError()
return $this->response->jsonError(
'Resource not found',
HttpStatusCode::NOT_FOUND
);
// З масивом помилок (наприклад валідація)
return $this->response->jsonError(
'Validation failed',
HttpStatusCode::UNPROCESSABLE_ENTITY,
['email' => ['The email field is required.']]
);
// { "status": "error", "code": 422, "message": "...", "errors": { ... } }
Ключ errors додається лише якщо масив не порожній.
API-маршрути
Для JSON-запитів підключайте на групу маршрутів (за потреби):
ParseJsonBodyMiddleware (body → getParsedBody()),
ForceJsonResponseMiddleware (Accept/Content-Type JSON).
Детальніше — middleware ядра.
RequestFormat::expectsJson() визначає, чи клієнт очікує JSON
(Accept: application/json або X-Requested-With: XMLHttpRequest).
Так само обробляється помилка валідації у middleware.
Редіректи
redirect($url)
return $this->response->redirect('/cabinet/profile', HttpStatusCode::FOUND);
Жорсткий шлях або повний URL. Статус за замовчуванням — 302 Found.
redirectByName()
return $this->response->redirectByName('admin.users');
return $this->response->redirectByName('admin.user.edit', ['id' => $user->getId()]);
URL будується через UrlGeneratorInterface::uri() — відносний шлях за
іменованим маршрутом. Зручно після POST (PRG).
back()
Безпечний повернення «назад» (не open redirect):
- Referer — якщо внутрішній (той самий host або відносний
/...) SAFE_BACK_URL— атрибут запиту зStorePreviousUrlMiddleware- Fallback — аргумент методу (за замовчуванням
/)
return $this->response->back();
return $this->response->back(HttpStatusCode::FOUND, '/cabinet');
Після невдалої валідації HandleValidationExceptionMiddleware викликає
back() для HTML-форм (flash + старі дані).
Порожня та довільна відповідь
use Concept\Core\Http\Protocol\HttpStatusCode;
$response = $this->response->createResponse(HttpStatusCode::NO_CONTENT);
// або CREATED, NOT_FOUND тощо — без тіла
createResponse() — з PSR-17; для кастомного тіла запишіть у
$response->getBody() і додайте заголовки через withHeader().
У звичайному коді достатньо методів фабрики вище.
Коди статусу
Константи в Concept\Core\Http\Protocol\HttpStatusCode:
| Константа | Код | Типове використання |
|---|---|---|
OK | 200 | Успішна відповідь |
CREATED | 201 | Створення ресурсу |
NO_CONTENT | 204 | Успіх без тіла |
FOUND | 302 | Redirect (за замовчуванням) |
NOT_FOUND | 404 | Ресурс не знайдено |
UNPROCESSABLE_ENTITY | 422 | Валідація FormRequest |
PAGE_EXPIRED | 419 | CSRF (CsrfException) |
INTERNAL_SERVER_ERROR | 500 | Необроблена помилка |
getReasonPhrase($code) — текст для reason phrase PSR-7.
Типові сценарії
POST → redirect (PRG)
public function store(StoreUserRequest $request): ResponseInterface
{
$this->userModel->newQuery()->create(
$this->normalizeUserPayload($request->validated())
);
$this->flashBag->addSuccess('User successfully created');
return $this->response->redirectByName('admin.users');
}
Не знайдено → flash + redirect
if (!($user instanceof UserModel)) {
$this->flashBag->addError('User not found');
return $this->response->redirectByName('admin.users');
}
return $this->viewResponse->create('@auth-admin/users/show', ['user' => $user]);
JSON endpoint
public function generateTokenApi(): ResponseInterface
{
return $this->response->json([
'token' => bin2hex(random_bytes(32)),
]);
}
Відповіді без контролера
Частина відповідей формується в middleware або error handler:
- Валідація —
HandleValidationExceptionMiddleware: JSON 422 абоback() - CSRF —
CsrfException(код 419), обробка error handler - Production / API помилки — Whoops +
jsonError()заRequestFormat
Не повертайте echo або header() з контролера — лише
ResponseInterface. Інакше emitter і middleware (наприклад DebugBar) не зможуть
коректно обробити відповідь.