156 lines
6.6 KiB
PHP
156 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Domains\Event\Actions\SignUp;
|
|
|
|
use App\Enumerations\EatingHabit;
|
|
use App\Enumerations\EfzStatus;
|
|
use App\EventPaymentModules\EventPaymentModuleRegistry;
|
|
use App\ValueObjects\Age;
|
|
use Illuminate\Support\Str;
|
|
|
|
class SignUpCommand {
|
|
public function __construct(public SignUpRequest $request) {
|
|
}
|
|
|
|
public function execute() : SignUpResponse {
|
|
$response = new SignUpResponse();
|
|
|
|
// Teilnehmer-Eingaben der gewählten Zahlungsart gegen das Modul-Schema absichern.
|
|
$module = $this->request->paymentMethod !== null
|
|
? EventPaymentModuleRegistry::forSlug($this->request->paymentMethod)
|
|
: null;
|
|
$paymentOptions = $module?->sanitizeParticipantOptions($this->request->paymentOptions) ?? [];
|
|
|
|
if ($module !== null && !$module->participantOptionsComplete($paymentOptions)) {
|
|
$response->success = false;
|
|
$response->message = 'Bitte alle erforderlichen Zahlungsangaben ausfüllen.';
|
|
return $response;
|
|
}
|
|
|
|
// Teilnahmeoptionen (event-spezifische Pflicht-Auswahl) gegen die Event-Definition absichern.
|
|
$participationOptionRows = $this->buildParticipationOptionRows();
|
|
if ($participationOptionRows === null) {
|
|
$response->success = false;
|
|
$response->message = 'Bitte alle Auswahlfelder ausfüllen.';
|
|
return $response;
|
|
}
|
|
|
|
$eatingHabit = match ($this->request->eating_habit) {
|
|
'vegan' => EatingHabit::EATING_HABIT_VEGAN,
|
|
'vegetarian' => EatingHabit::EATING_HABIT_VEGETARIAN,
|
|
default => EatingHabit::EATING_HABIT_OMNIVOR,
|
|
};
|
|
|
|
$participantAge = new Age($this->request->birthday);
|
|
|
|
$response->participant = $this->request->event->participants()->create(
|
|
[
|
|
'tenant' => $this->request->event->tenant,
|
|
'user_id' => $this->request->user_id,
|
|
'identifier' => Str::random(10),
|
|
'firstname' => $this->request->firstname,
|
|
'lastname' => $this->request->lastname,
|
|
'nickname' => $this->request->nickname,
|
|
'participation_type' => $this->request->participationType,
|
|
'local_group' => $this->request->localGroup->slug,
|
|
'birthday' => $this->request->birthday,
|
|
'address_1' => $this->request->address_1,
|
|
'address_2' => $this->request->address_2,
|
|
'postcode' => $this->request->postcode,
|
|
'city' => $this->request->city,
|
|
'email_1' => $this->request->email_1,
|
|
'email_2' => $this->request->email_2,
|
|
'phone_1' => $this->request->phone_1,
|
|
'phone_2' => $this->request->phone_2,
|
|
'contact_person' => $this->request->contact_person,
|
|
'allergies' => $this->request->allergies,
|
|
'intolerances' => $this->request->intolerances,
|
|
'medications' => $this->request->medications,
|
|
'tetanus_vaccination' => $this->request->tetanus_vaccination,
|
|
'eating_habit' => $eatingHabit,
|
|
'swimming_permission' => $participantAge->isfullAged() ? 'SWIMMING_PERMISSION_ALLOWED' : $this->request->swimming_permission,
|
|
'first_aid_permission' => $participantAge->isfullAged() ? 'FIRST_AID_PERMISSION_ALLOWED' : $this->request->first_aid_permission,
|
|
'foto_socialmedia' => $this->request->foto_socialmedia,
|
|
'foto_print' => $this->request->foto_print,
|
|
'foto_webseite' => $this->request->foto_webseite,
|
|
'foto_partner' => $this->request->foto_partner,
|
|
'foto_intern' => $this->request->foto_intern,
|
|
'arrival_date' => $this->request->arrival,
|
|
'departure_date' => $this->request->departure,
|
|
'arrival_eating' => $this->request->arrival_eating,
|
|
'departure_eating' => $this->request->departure_eating,
|
|
'notes' => $this->request->notes,
|
|
'amount' => $this->request->amount,
|
|
'payment_purpose' => $this->request->event->name . ' - Beitrag ' . $this->request->firstname . ' ' . $this->request->lastname,
|
|
'payment_method' => $this->request->paymentMethod,
|
|
'payment_options' => $paymentOptions,
|
|
'efz_status' => $participantAge->isfullAged() ? EfzStatus::EFZ_STATUS_NOT_CHECKED : EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
|
]
|
|
);
|
|
|
|
$this->persistParticipationOptions($response->participant, $participationOptionRows);
|
|
|
|
$response->success = true;
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Validiert die Antworten gegen die am Event definierten Teilnahmeoptionen und erzeugt die zu speichernden
|
|
* Zeilen mit Label-Snapshots. Gibt null zurück, wenn eine Pflicht-Frage unbeantwortet oder eine Antwort
|
|
* ungültig ist (Absicherung gegen manipulierte Payloads).
|
|
*
|
|
* @return array<int, array{question_key: string, question_label: string, value: string, value_label: string}>|null
|
|
*/
|
|
private function buildParticipationOptionRows(): ?array
|
|
{
|
|
$rows = [];
|
|
|
|
foreach ($this->request->event->participation_options ?? [] as $question) {
|
|
$key = $question['key'] ?? null;
|
|
if ($key === null) {
|
|
continue;
|
|
}
|
|
|
|
$value = $this->request->participationOptions[$key] ?? null;
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
$option = null;
|
|
foreach ($question['options'] ?? [] as $candidate) {
|
|
if (($candidate['value'] ?? null) === $value) {
|
|
$option = $candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($option === null) {
|
|
return null;
|
|
}
|
|
|
|
$rows[] = [
|
|
'question_key' => $key,
|
|
'question_label' => $question['label'] ?? $key,
|
|
'value' => $value,
|
|
'value_label' => $option['label'] ?? $value,
|
|
];
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, array{question_key: string, question_label: string, value: string, value_label: string}> $rows
|
|
*/
|
|
private function persistParticipationOptions(\App\Models\EventParticipant $participant, array $rows): void
|
|
{
|
|
foreach ($rows as $row) {
|
|
$participant->selectedOptions()->create(array_merge($row, [
|
|
'tenant' => $this->request->event->tenant,
|
|
'event_id' => $this->request->event->id,
|
|
]));
|
|
}
|
|
}
|
|
|
|
}
|