*/ #[Assert\Valid] public array $roomSelections = []; /** * @var array */ #[Assert\Valid] public array $participants = []; #[Assert\Choice( choices: [Constants::PAYMENT_METHOD_TRANSFER, Constants::PAYMENT_METHOD_DEBIT], message: 'Bitte wählen Sie eine gültige Zahlungsart.' )] public ?string $paymentMethod = Constants::PAYMENT_METHOD_TRANSFER; public ?BankAccountDto $bankAccount = null; public ?int $agencyId = null; public function __construct(public Travel $travel, public int $hotelId) { } /** * @return array */ public function getSelectedRooms(): array { return array_filter($this->roomSelections, function (RoomSelectionDto $roomSelection) { return 0 < $roomSelection->quantity; }); } public function getParticipants(): array { return $this->participants; } public function hasParticipant(int $index): bool { return isset($this->participants[$index]); } public function getParticipant(int $index): ?ParticipantDto { return $this->participants[$index] ?? null; } /** * Determines if this is a family booking based on participant age distribution. * * A family booking is defined as: * - 1 or 2 participants aged 18 or older (adults) * - At least 1 participant younger than 18 (children) * * @return bool True if this qualifies as a family booking */ public function isFamilyBooking(): bool { $adults = 0; // Count of participants >= 18 years $children = 0; // Count of participants < 18 years // Use travel start date for age calculation $travelStartDate = $this->travel->dateFrom; foreach ($this->participants as $participant) { $age = $participant->getAge($travelStartDate); if (null === $age) { continue; // Skip participants without birth date } if ($age >= 18) { ++$adults; } else { ++$children; } } return ($adults >= 1 && $adults <= 2) && ($children >= 1); } #[Assert\Callback(callback: 'validateRoomSelection', groups: ['booking_create_step_1'])] public function validateRoomSelection(ExecutionContextInterface $context): void { $selectedRooms = $this->getSelectedRooms(); if (0 === count($selectedRooms)) { $context->buildViolation('Bitte mindestens ein Zimmer/Bett auswählen') ->addViolation(); } } #[Assert\Callback] public function validateBankAccount(ExecutionContextInterface $context): void { if (Constants::PAYMENT_METHOD_DEBIT !== $this->paymentMethod) { return; } if (null === $this->bankAccount) { $context->buildViolation('Bitte geben Sie Ihre Bankverbindung an.') ->atPath('bankAccount') ->addViolation(); return; } // Validate IBAN if (null === $this->bankAccount->iban || '' === trim($this->bankAccount->iban)) { $context->buildViolation('Bitte geben Sie Ihre IBAN ein.') ->atPath('bankAccount.iban') ->addViolation(); } // Validate account holder if (null === $this->bankAccount->accountHolder || '' === trim($this->bankAccount->accountHolder)) { $context->buildViolation('Bitte geben Sie den Kontoinhaber ein.') ->atPath('bankAccount.accountHolder') ->addViolation(); } // Validate SEPA mandate if (false === $this->bankAccount->sepaMandateAccepted) { $context->buildViolation('Bitte akzeptieren Sie das SEPA-Mandat.') ->atPath('bankAccount.sepaMandateAccepted') ->addViolation(); } } }