M1: CRUD-Slice 'Mahlzeit' Schritt 2 – UI (Controller, Templates, Routen, Feature-Tests)
This commit is contained in:
@@ -22,7 +22,7 @@ final class EntityManagerTransactionManager implements TransactionManagerInterfa
|
||||
public function execute(\Closure $work): mixed
|
||||
{
|
||||
try {
|
||||
return $this->em->transactional(static fn () => $work($work));
|
||||
return $this->em->wrapInTransaction($work);
|
||||
} catch (\Throwable $e) {
|
||||
$this->em->clear();
|
||||
throw $e;
|
||||
|
||||
@@ -11,7 +11,7 @@ interface TransactionManagerInterface
|
||||
* Bei Exception wird zurückgerollt, ansonsten committiert.
|
||||
*
|
||||
* @template T
|
||||
* @param callable(\Closure|\Throwable): T $work
|
||||
* @param \Closure(): T $work
|
||||
* @return T
|
||||
* @throws \Throwable
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\UI\Http\Essen;
|
||||
|
||||
use App\Logic\Essen\Dto\MahlzeitCreateRequest;
|
||||
use App\Logic\Essen\Dto\MahlzeitListRequest;
|
||||
use App\Logic\Essen\Dto\MahlzeitUpdateRequest;
|
||||
use App\Logic\Essen\Manager\MahlzeitManagerInterface;
|
||||
use App\Logic\Essen\Model\Mahlzeit;
|
||||
use App\Logic\Essen\Model\Wochentag;
|
||||
use App\Logic\Essen\Query\MahlzeitListQuery;
|
||||
use App\Logic\Essen\UseCase\MahlzeitCreateUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitDeleteUseCase;
|
||||
use App\Logic\Essen\UseCase\MahlzeitUpdateUseCase;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* CRUD-Slice "Mahlzeit" (M1) – Web-UI.
|
||||
*
|
||||
* Stil: klassisches POST/Redirect/Flash (htmx-Toggles kommen in M2).
|
||||
*
|
||||
* - Reads über `MahlzeitListQuery` / `MahlzeitManagerInterface::find`.
|
||||
* - Writes über die UseCases (Create/Update/Delete).
|
||||
* - Syntaktische Pflichtfeld-Prüfung (Name) passiert hier im UI-Layer
|
||||
* (rein PHP, s. architektur.md §1.13); fachliche Invarianten bleiben
|
||||
* im UseCase/Model und laufen zentral über den ExceptionSubscriber.
|
||||
*/
|
||||
final class MahlzeitController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly MahlzeitCreateUseCase $create,
|
||||
private readonly MahlzeitUpdateUseCase $update,
|
||||
private readonly MahlzeitDeleteUseCase $delete,
|
||||
private readonly MahlzeitListQuery $list,
|
||||
private readonly MahlzeitManagerInterface $mahlzeiten,
|
||||
) {}
|
||||
|
||||
/** GET /mahlzeiten — Planungsliste. */
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('mahlzeiten/index.html.twig', [
|
||||
'mahlzeiten' => $this->list->execute(new MahlzeitListRequest())->items,
|
||||
]);
|
||||
}
|
||||
|
||||
/** GET /mahlzeiten/neu — Anlegen-Formular. */
|
||||
public function neu(): Response
|
||||
{
|
||||
return $this->render('mahlzeiten/form.html.twig', $this->formKontext(null, null, [
|
||||
'name' => '',
|
||||
'notiz' => '',
|
||||
'wochentage' => [],
|
||||
]));
|
||||
}
|
||||
|
||||
/** POST /mahlzeiten — Mahlzeit anlegen. */
|
||||
public function speichern(Request $request): Response
|
||||
{
|
||||
[$name, $notiz, $wochentage] = $this->binde($request);
|
||||
|
||||
if ($name === '') {
|
||||
return $this->render('mahlzeiten/form.html.twig', $this->formKontext(
|
||||
null,
|
||||
'Name ist ein Pflichtfeld.',
|
||||
['name' => '', 'notiz' => $notiz, 'wochentage' => $wochentage],
|
||||
));
|
||||
}
|
||||
|
||||
$this->create->execute(new MahlzeitCreateRequest(name: $name, notiz: $notiz, wochentage: $wochentage));
|
||||
$this->addFlash('notice', sprintf('Mahlzeit „%s“ angelegt.', $name));
|
||||
|
||||
return $this->redirectToRoute('mahlzeiten_index');
|
||||
}
|
||||
|
||||
/** GET /mahlzeiten/{id}/bearbeiten — Bearbeiten-Formular. */
|
||||
public function bearbeiten(int $id): Response
|
||||
{
|
||||
$mahlzeit = $this->mahlzeiten->find($id);
|
||||
|
||||
return $this->render('mahlzeiten/form.html.twig', $this->formKontext($mahlzeit, null, [
|
||||
'name' => $mahlzeit->name,
|
||||
'notiz' => $mahlzeit->notiz,
|
||||
'wochentage' => $mahlzeit->wochentage,
|
||||
]));
|
||||
}
|
||||
|
||||
/** POST /mahlzeiten/{id} — Mahlzeit bearbeiten. */
|
||||
public function aktualisieren(Request $request, int $id): Response
|
||||
{
|
||||
$mahlzeit = $this->mahlzeiten->find($id);
|
||||
[$name, $notiz, $wochentage] = $this->binde($request);
|
||||
|
||||
if ($name === '') {
|
||||
return $this->render('mahlzeiten/form.html.twig', $this->formKontext(
|
||||
$mahlzeit,
|
||||
'Name ist ein Pflichtfeld.',
|
||||
['name' => '', 'notiz' => $notiz, 'wochentage' => $wochentage],
|
||||
));
|
||||
}
|
||||
|
||||
$this->update->execute($id, new MahlzeitUpdateRequest(name: $name, notiz: $notiz, wochentage: $wochentage));
|
||||
$this->addFlash('notice', sprintf('Mahlzeit „%s“ aktualisiert.', $name));
|
||||
|
||||
return $this->redirectToRoute('mahlzeiten_index');
|
||||
}
|
||||
|
||||
/** POST /mahlzeiten/{id}/loeschen — Mahlzeit hart löschen. */
|
||||
public function loeschen(Request $request, int $id): Response
|
||||
{
|
||||
$mahlzeit = $this->mahlzeiten->find($id);
|
||||
$this->delete->execute($id);
|
||||
$this->addFlash('notice', sprintf('Mahlzeit „%s“ gelöscht.', $mahlzeit->name));
|
||||
|
||||
return $this->redirectToRoute('mahlzeiten_index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Bindet die Formularfelder (Name/Notiz/Wochentage) aus dem Request.
|
||||
*
|
||||
* @return array{0: string, 1: string, 2: list<Wochentag>}
|
||||
*/
|
||||
private function binde(Request $request): array
|
||||
{
|
||||
$name = trim((string) $request->request->get('name', ''));
|
||||
$notiz = (string) $request->request->get('notiz', '');
|
||||
$wochentage = Wochentag::ausWerten((array) $request->request->all('wochentage'));
|
||||
|
||||
return [$name, $notiz, $wochentage];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{name: string, notiz: string, wochentage: list<Wochentag>} $werte
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function formKontext(?Mahlzeit $mahlzeit, ?string $fehler, array $werte): array
|
||||
{
|
||||
return [
|
||||
'mahlzeit' => $mahlzeit,
|
||||
'fehler' => $fehler,
|
||||
'werte' => $werte,
|
||||
'wochentage_choices' => Wochentag::alle(),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user