booking; $travelData = $formData->travel; $this->resetServiceMappings($bookingData); foreach ($formData->participants as $participant) { $this->processParticipantServices($participant, $bookingData, $travelData); } $this->removeUnusedServices($bookingData); $this->updateParticipantPersonalData($formData->participants, $bookingData); $this->syncApplicantData($bookingData); $payload = $this->buildBasePayload($bookingData); $this->addBankAccountToPayload($payload, $bookingData); $this->buildParticipantPayload($payload, $bookingData); $this->buildServicesPayload($payload, $bookingData); $this->buildPickupPayload($payload, $bookingData); return $payload; } /** * Resets all existing participant-to-service mappings to start with a clean slate. * * This ensures that service assignments are rebuilt from scratch based on current form selections. * * @param object $bookingData The booking data object containing services to reset */ private function resetServiceMappings(object $bookingData): void { $servicesToReset = [ ...$bookingData->additionalServices, ...$bookingData->transportationServices, ...$bookingData->pickupsOutbound, ...$bookingData->pickupsInbound, ]; foreach ($servicesToReset as $service) { $service->mapping = []; } } /** * Processes all services for a single participant. * * This orchestrator method handles the complete service assignment workflow for one participant, * including additional services, transportation services, and pickup locations. * * @param object $participant The participant data from the form * @param object $bookingData The booking data object to update * @param object $travelData The travel data containing available services */ private function processParticipantServices(object $participant, object $bookingData, object $travelData): void { if (true === $participant->isCanceled()) { return; } $this->processAdditionalServices($participant, $bookingData, $travelData); $this->processTransportationServices($participant, $bookingData, $travelData); $this->processPickupLocations($participant, $bookingData); } /** * Processes additional services for a participant. * * Maps additional services (courses, ski passes, board options, rentals) to the participant. * Adds new services to the booking if they don't already exist and sets individual pricing. * * @param object $participant The participant data from the form * @param object $bookingData The booking data object to update * @param object $travelData The travel data containing available services */ private function processAdditionalServices(object $participant, object $bookingData, object $travelData): void { $servicesToMap = [ ...$participant->courses, ...$participant->additionalServices, ...$participant->board, ...$participant->rentals, ]; // Add ski pass if selected (single service, not an array) if (null !== $participant->skiPass) { $servicesToMap[] = $participant->skiPass; } foreach ($servicesToMap as $service) { if (false === isset($bookingData->additionalServices[$service->id])) { $serviceToAdd = $travelData->additionalServices[$service->id] ?? null; if (null !== $serviceToAdd) { $bookingData->additionalServices[$service->id] = $serviceToAdd; $bookingData->additionalServices[$service->id]->individualPrice[$participant->index] = $serviceToAdd->price; } } $bookingData->additionalServices[$service->id]->mapping[] = $participant->index; } } /** * Processes transportation services for a participant. * * Maps transportation services (both directions: to and from destination) to the participant. * Adds new transportation services to the booking if they don't already exist. * * @param object $participant The participant data from the form * @param object $bookingData The booking data object to update * @param object $travelData The travel data containing available services */ private function processTransportationServices(object $participant, object $bookingData, object $travelData): void { foreach ([$participant->transportationOutbound, $participant->transportationInbound] as $service) { if (false === isset($bookingData->transportationServices[$service->id])) { $serviceToAdd = $travelData->transportationServices[$service->id] ?? null; if (null !== $serviceToAdd) { $bookingData->transportationServices[$service->id] = $serviceToAdd; $bookingData->transportationServices[$service->id]->individualPrice[$participant->index] = $serviceToAdd->price; } } $bookingData->transportationServices[$service->id]->mapping[] = $participant->index; } } /** * Processes pickup locations for participants using bus transportation. * * Only processes pickup locations for bus transportation services and maps the participant * to their selected pickup location. * * @param object $participant The participant data from the form * @param object $bookingData The booking data object to update */ private function processPickupLocations(object $participant, object $bookingData): void { if ('BUS' === $participant->transportationOutbound->subType && null !== $selectedPickup = $participant->pickupOutbound) { if (false === isset($bookingData->pickupsOutbound[$selectedPickup->id])) { $bookingData->pickupsOutbound[$selectedPickup->id] = $selectedPickup; } $bookingData->pickupsOutbound[$selectedPickup->id]->mapping[] = $participant->index; } } /** * Removes services and pickups with no participant mappings. * * Cleans up unused services to prevent empty services from being sent to the API. * This includes additional services, transportation services, and pickup locations. * * @param object $bookingData The booking data object to clean up */ private function removeUnusedServices(object $bookingData): void { foreach ($bookingData->additionalServices as $service) { if (0 === count($service->mapping)) { unset($bookingData->additionalServices[$service->id]); } } foreach ($bookingData->transportationServices as $service) { if (0 === count($service->mapping)) { unset($bookingData->transportationServices[$service->id]); } } foreach ($bookingData->pickupsOutbound as $pickup) { if (0 === count($pickup->mapping)) { unset($bookingData->pickupsOutbound[$pickup->id]); } } foreach ($bookingData->pickupsInbound as $pickup) { if (0 === count($pickup->mapping)) { unset($bookingData->pickupsInbound[$pickup->id]); } } } /** * Updates participant personal data from form input. * * Only processes participants with status 'F' (active/confirmed participants). * Updates all personal data fields and communication information. * * @param array $participants The participants array from the form * @param object $bookingData The booking data object to update */ private function updateParticipantPersonalData(array $participants, object $bookingData): void { foreach ($participants as $participant) { if ('F' !== $participant->status) { continue; } $bookingData->participants[$participant->index]->firstName = $participant->firstName; $bookingData->participants[$participant->index]->name = $participant->lastName; $bookingData->participants[$participant->index]->dateOfBirth = $participant->dateOfBirth; $bookingData->participants[$participant->index]->gender = $participant->gender; $bookingData->participants[$participant->index]->nationality = $participant->nationality; $bookingData->participants[$participant->index]->height = $participant->height; $bookingData->participants[$participant->index]->weight = $participant->weight; $bookingData->participants[$participant->index]->shoeSize = $participant->shoeSize; if ($participant->email || $participant->mobile) { if (null === $bookingData->participants[$participant->index]->communication) { $bookingData->participants[$participant->index]->communication = new Communication(); } $bookingData->participants[$participant->index]->communication->email = $participant->email; $bookingData->participants[$participant->index]->communication->mobile = $participant->mobile; } } } /** * Synchronizes applicant data with first participant's physical characteristics. * * The applicant (booking holder) inherits physical data from the first participant. * * @param object $bookingData The booking data object to update */ private function syncApplicantData(object $bookingData): void { if (false !== $firstParticipant = reset($bookingData->participants)) { $bookingData->applicant->height = $firstParticipant->height; $bookingData->applicant->weight = $firstParticipant->weight; $bookingData->applicant->shoeSize = $firstParticipant->shoeSize; } } /** * Builds the base API payload structure with booking information. * * Creates the main structure that will be populated with detailed data sections. * * @param object $bookingData The booking data object * * @return array The base payload structure */ private function buildBasePayload(object $bookingData): array { return [ 'idbuchung' => $bookingData->id, 'status' => $bookingData->status, 'idagentur' => $bookingData->agencyId, 'idreise' => $bookingData->dateId, 'idpartner' => $bookingData->hotelId, 'anmelder' => $bookingData->applicant->toPayload(), 'zahlung' => [ '@idzahlungsart' => $bookingData->paymentId, '@bezeichnung' => $bookingData->paymentLabel, '@art' => $bookingData->paymentType, ], 'teilnehmerliste' => [ 'teilnehmer' => [], ], 'zusatzleistungen' => [ 'zusatzleistung' => [], ], 'beförderungen' => [ 'beförderung' => [], ], 'ferienzielunterbringungen' => [ 'ferienzielunterbringung' => [], ], ]; } /** * Adds bank account information to the payload if present. * * Bank account information is required for direct debit payments. * * @param array $payload The payload array to modify * @param object $bookingData The booking data object */ private function addBankAccountToPayload(array &$payload, object $bookingData): void { if (null !== $bookingData->bankAccount) { $payload['zahlung']['bankverbindung'] = [ '@kreditinstitut' => $bookingData->bankAccount->bankName, '@iban' => $bookingData->bankAccount->iban, '@bic' => $bookingData->bankAccount->bic, '@kontoinhaber' => $bookingData->bankAccount->holder, ]; } } /** * Builds the participant list section of the payload. * * Includes status and personal data for each participant. * * @param array $payload The payload array to modify * @param object $bookingData The booking data object */ private function buildParticipantPayload(array &$payload, object $bookingData): void { foreach ($bookingData->participants as $index => $participant) { $payload['teilnehmerliste']['teilnehmer'][] = [ '@id' => $index + 1, 'status' => $bookingData->participantsStatus[$index], ...$participant->toPayload(), ]; } } /** * Builds the services sections of the payload. * * Includes additional services, transportation services, and accommodation details * with participant mappings and quantities. * * @param array $payload The payload array to modify * @param object $bookingData The booking data object */ private function buildServicesPayload(array &$payload, object $bookingData): void { foreach ($bookingData->additionalServices as $service) { $payload['zusatzleistungen']['zusatzleistung'][] = [ '@idleistung' => $service->id, '@anzahl' => count($service->mapping), '@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $service->mapping)), ]; } foreach ($bookingData->transportationServices as $service) { $payload['beförderungen']['beförderung'][] = [ '@idleistung' => $service->id, '@anzahl' => count($service->mapping), '@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $service->mapping)), ]; } foreach ($bookingData->rooms as $room) { $payload['ferienzielunterbringungen']['ferienzielunterbringung'][] = [ '@idzimmer' => $room->id, '@kategorie' => $room->category, '@idverpflegung' => $room->boardId, '@anreise' => $room->dateFrom ? $room->dateFrom->format('d.m.Y') : null, '@abreise' => $room->dateTo ? $room->dateTo->format('d.m.Y') : null, '@anzahl' => $room->totalCount, '@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $room->mapping)), ]; } } /** * Builds the pickup locations section of the payload. * * Only included in payload if there are actual pickup assignments for bus transportation. * * @param array $payload The payload array to modify * @param object $bookingData The booking data object */ private function buildPickupPayload(array &$payload, object $bookingData): void { if (0 < count($bookingData->pickupsOutbound)) { $payload['zustiege']['zustieg'] = []; foreach ($bookingData->pickupsOutbound as $pickup) { $payload['zustiege']['zustieg'][] = [ '@idzustieg' => $pickup->id, '@anzahl' => count($pickup->mapping), '@zuordnung' => implode(',', array_map(fn ($index) => $index + 1, $pickup->mapping)), ]; } } } }