Files
svc-essensplanung/src/UI/Http/Api/MahlzeitApiController.php
T

105 lines
3.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\UI\Http\Api;
use App\Logic\Essen\Dto\MahlzeitCreateRequest;
use App\Logic\Essen\Dto\MahlzeitListRequest;
use App\Logic\Essen\Model\MahlzeitStatus;
use App\Logic\Essen\Model\Wochentag;
use App\Logic\Essen\Query\MahlzeitListQuery;
use App\Logic\Essen\UseCase\MahlzeitCheckOffUseCase;
use App\Logic\Essen\UseCase\MahlzeitCreateUseCase;
use App\Logic\Essen\UseCase\MahlzeitUncheckUseCase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* Stateless JSON-API für Consumer wie Home Assistant (Auth via `X-Api-Token`,
* s. `ApiTokenSubscriber`).
*
* - `GET /api/mahlzeiten` – Liste, optional `?status=geplant|gegessen`.
* - `POST /api/mahlzeiten/{id}/checkoff` – abhaken (geplant → gegessen).
* - `POST /api/mahlzeiten/{id}/uncheck` – aufheben (gegessen → geplant).
* - `POST /api/mahlzeiten` – anlegen (JSON `name` Pflicht, `notiz`/`wochentage` optional).
*
* Fachregeln laufen über dieselben UseCases/Queries wie die Web-UI;
* Fehler werden vom `ExceptionSubscriber` als JSON gerendert.
*/
final readonly class MahlzeitApiController
{
public function __construct(
private MahlzeitListQuery $list,
private MahlzeitCheckOffUseCase $checkOff,
private MahlzeitUncheckUseCase $uncheck,
private MahlzeitCreateUseCase $create,
) {}
/** @param non-empty-string $name – für die Route-Signatur, hier nur Doku */
public function index(Request $request): JsonResponse
{
$status = null;
$filter = (string) $request->query->get('status', '');
if ($filter !== '') {
$status = MahlzeitStatus::tryFrom($filter);
if ($status === null) {
throw new BadRequestHttpException('status muss "geplant" oder "gegessen" sein.');
}
}
$items = $this->list->execute(new MahlzeitListRequest(status: $status))->items;
return new JsonResponse([
'mahlzeiten' => \array_map(self::serialize(...), $items),
]);
}
public function checkoff(int $id): JsonResponse
{
return new JsonResponse(self::serialize($this->checkOff->execute($id)));
}
public function uncheck(int $id): JsonResponse
{
return new JsonResponse(self::serialize($this->uncheck->execute($id)));
}
public function create(Request $request): JsonResponse
{
$payload = json_decode((string) $request->getContent(), true);
if (!\is_array($payload)) {
throw new BadRequestHttpException('JSON-Body erwartet, z. B. {"name": "Bockbrot"}');
}
$name = \trim((string) ($payload['name'] ?? ''));
if ($name === '') {
throw new BadRequestHttpException('name ist ein Pflichtfeld.');
}
$notiz = \trim((string) ($payload['notiz'] ?? ''));
$wochentage = Wochentag::ausWerten((array) ($payload['wochentage'] ?? []));
return new JsonResponse(self::serialize($this->create->execute(new MahlzeitCreateRequest(
name: $name,
notiz: $notiz,
wochentage: $wochentage,
))->mahlzeit), 201);
}
/** Serialisiert ein Read-Modell in das API-Format. */
public static function serialize(object $m): array
{
return [
'id' => $m->id,
'name' => $m->name,
'notiz' => $m->notiz,
'wochentage' => \array_map(static fn (Wochentag $t): string => $t->value, $m->wochentage),
'status' => $m->status->value,
'sort_order' => $m->sortOrder,
'gegessen_am' => $m->gegessenAm?->format(\DateTimeInterface::RFC3339),
];
}
}