Rich-Model Mahlzeit (MahlzeitStatus, Wochentag) mit Invarianten und Zustandsübergängen; DTOs + MahlzeitFactory (reine Mapping-Funktionen); Manager (create hängt an Ende an: maxSortOrder+1, find → 404) und Create/Update/Delete-UseCases mit Transaktion + Events nach Commit. Data-Layer: MahlzeitEntity (Tabelle essens_mahlzeiten, wochentage als CSV), Mapper (CSV/Enum/null), Provider (find/list/maxSortOrder), Processor (create/save/delete). DI-Bindings in services.yaml. ClockInterface (Logic) + SystemClock (Data). Tests: Model/Factory/UseCase-Unit + Provider/Processor/Mapper-Integration (In-Mem-SQLite). composer test grün (53 Tests, 161 Assertions).
68 lines
2.1 KiB
PHP
68 lines
2.1 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Logic\Essen\Mapping;
|
||
|
||
use App\Logic\Common\Exception\BusinessRuleViolationException;
|
||
use App\Logic\Essen\Dto\MahlzeitCreateRequest;
|
||
use App\Logic\Essen\Dto\MahlzeitUpdateRequest;
|
||
use App\Logic\Essen\Model\Mahlzeit;
|
||
|
||
/**
|
||
* Reine Funktionen: Request-DTO → Modell. Keine Framework-/DB-Abhängigkeit.
|
||
*
|
||
* Fachregeln (Pflichtfelder) werden hier als Business-Validierung geprüft.
|
||
* Zeitstempel (`erstelltAm`/`aktualisiertAm`) liefert der UseCase über `ClockInterface`.
|
||
*/
|
||
final class MahlzeitFactory
|
||
{
|
||
/**
|
||
* Erstellt ein neues, geplantes Modell aus einem Anlegen-DTO.
|
||
*
|
||
* `sortOrder` wird bewusst auf 0 gesetzt – der Manager hängt den Eintrag
|
||
* anschließend am Ende an (max+1).
|
||
*/
|
||
public function createFromCreateRequest(MahlzeitCreateRequest $req, \DateTimeImmutable $jetzt): Mahlzeit
|
||
{
|
||
$this->assertRequired($req->name, 'name');
|
||
|
||
return Mahlzeit::anlegen(
|
||
name: $req->name,
|
||
notiz: $req->notiz,
|
||
wochentage: $req->wochentage,
|
||
sortOrder: 0,
|
||
jetzt: $jetzt,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Wendet ein Teil-Update auf ein bestehendes Modell an. `null`-Felder bleiben unverändert.
|
||
*/
|
||
public function updateFromUpdateRequest(Mahlzeit $mahlzeit, MahlzeitUpdateRequest $req, \DateTimeImmutable $jetzt): Mahlzeit
|
||
{
|
||
if ($req->name !== null) {
|
||
$this->assertRequired($req->name, 'name');
|
||
}
|
||
|
||
$name = $req->name ?? $mahlzeit->name;
|
||
$notiz = $req->notiz ?? $mahlzeit->notiz;
|
||
$wochentage = $req->wochentage ?? $mahlzeit->wochentage;
|
||
|
||
return $mahlzeit->aktualisieren(
|
||
name: $name,
|
||
notiz: $notiz,
|
||
wochentage: $wochentage,
|
||
sortOrder: $mahlzeit->sortOrder,
|
||
jetzt: $jetzt,
|
||
);
|
||
}
|
||
|
||
private function assertRequired(?string $value, string $feld): void
|
||
{
|
||
if ($value === null || \trim($value) === '') {
|
||
throw new BusinessRuleViolationException(\sprintf('%s ist ein Pflichtfeld.', $feld));
|
||
}
|
||
}
|
||
}
|