116 lines
3.4 KiB
PHP
116 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Event\Actions\SetParticipationOptions;
|
|
|
|
use Illuminate\Support\Str;
|
|
|
|
class SetParticipationOptionsCommand
|
|
{
|
|
public function __construct(private SetParticipationOptionsRequest $request)
|
|
{
|
|
}
|
|
|
|
public function execute(): SetParticipationOptionsResponse
|
|
{
|
|
$response = new SetParticipationOptionsResponse();
|
|
|
|
$this->request->event->participation_options = $this->normalize($this->request->questions);
|
|
$this->request->event->save();
|
|
|
|
$response->success = true;
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Normalisiert die rohe Fragen-Definition: trimmt Labels, generiert stabile Keys/Values und verwirft
|
|
* unvollständige Fragen (kein Label oder keine gültige Option). Keys/Values sind innerhalb ihres
|
|
* Geltungsbereichs eindeutig, damit Antworten robust referenzieren können.
|
|
*
|
|
* @param array<int, array<string, mixed>> $questions
|
|
* @return array<int, array{key: string, title: string, label: string, options: array<int, array{value: string, label: string}>}>
|
|
*/
|
|
private function normalize(array $questions): array
|
|
{
|
|
$normalized = [];
|
|
$usedKeys = [];
|
|
|
|
foreach ($questions as $question) {
|
|
// Titel ist Pflicht (Überschrift/Feldbeschriftung); die Frage ist optionaler Erklärtext.
|
|
$title = trim((string)($question['title'] ?? ''));
|
|
if ($title === '') {
|
|
continue;
|
|
}
|
|
|
|
$label = trim((string)($question['label'] ?? ''));
|
|
|
|
$options = $this->normalizeOptions($question['options'] ?? []);
|
|
if ($options === []) {
|
|
continue;
|
|
}
|
|
|
|
$key = $this->uniqueSlug((string)($question['key'] ?? ''), $title, $usedKeys);
|
|
$usedKeys[] = $key;
|
|
|
|
$normalized[] = [
|
|
'key' => $key,
|
|
'title' => $title,
|
|
'label' => $label,
|
|
'options' => $options,
|
|
];
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array<string, mixed>> $options
|
|
* @return array<int, array{value: string, label: string}>
|
|
*/
|
|
private function normalizeOptions(mixed $options): array
|
|
{
|
|
if (!is_array($options)) {
|
|
return [];
|
|
}
|
|
|
|
$normalized = [];
|
|
$usedValues = [];
|
|
|
|
foreach ($options as $option) {
|
|
$label = trim((string)($option['label'] ?? ''));
|
|
if ($label === '') {
|
|
continue;
|
|
}
|
|
|
|
$value = $this->uniqueSlug((string)($option['value'] ?? ''), $label, $usedValues);
|
|
$usedValues[] = $value;
|
|
|
|
$normalized[] = ['value' => $value, 'label' => $label];
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* Liefert einen eindeutigen Slug: bevorzugt den bestehenden Wert (Bestandsschutz für schon gespeicherte
|
|
* Fragen/Antworten), sonst aus dem Label abgeleitet; Kollisionen werden durchnummeriert.
|
|
*
|
|
* @param array<int, string> $used
|
|
*/
|
|
private function uniqueSlug(string $existing, string $label, array $used): string
|
|
{
|
|
$base = $existing !== '' ? $existing : Str::slug($label);
|
|
if ($base === '') {
|
|
$base = 'option';
|
|
}
|
|
|
|
$candidate = $base;
|
|
$suffix = 2;
|
|
while (in_array($candidate, $used, true)) {
|
|
$candidate = $base . '-' . $suffix;
|
|
$suffix++;
|
|
}
|
|
|
|
return $candidate;
|
|
}
|
|
}
|