request->event->addons = $this->normalize($this->request->addons); $this->request->event->save(); $response->success = true; return $response; } /** * Normalisiert die rohe Addon-Definition: trimmt Titel/Beschreibung, parst den Preis (≥ 0), erzwingt den * Titel als Pflichtfeld und generiert stabile Keys (Bestandsschutz für bereits gespeicherte Buchungen). * * @param array> $addons * @return array */ private function normalize(array $addons): array { $normalized = []; $usedKeys = []; foreach ($addons as $addon) { $title = trim((string)($addon['title'] ?? '')); if ($title === '') { continue; } $price = Amount::fromString((string)($addon['price'] ?? '0'))->getAmount(); if ($price < 0) { $price = 0.0; } $key = $this->uniqueSlug((string)($addon['key'] ?? ''), $title, $usedKeys); $usedKeys[] = $key; $normalized[] = [ 'key' => $key, 'title' => $title, 'description' => trim((string)($addon['description'] ?? '')), 'price' => round($price, 2), 'flat' => (bool)($addon['flat'] ?? false), ]; } return $normalized; } /** * Liefert einen eindeutigen Slug: bevorzugt den bestehenden Wert, sonst aus dem Titel abgeleitet; * Kollisionen werden durchnummeriert. * * @param array $used */ private function uniqueSlug(string $existing, string $title, array $used): string { $base = $existing !== '' ? $existing : Str::slug($title); if ($base === '') { $base = 'addon'; } $candidate = $base; $suffix = 2; while (in_array($candidate, $used, true)) { $candidate = $base . '-' . $suffix; $suffix++; } return $candidate; } }