extractUserData($bookingDto))); } /** * Extracts all user-editable data from a BookingDto as a serializable array. * * This method captures all data that represents user input: personal information, * addresses, body dimensions, service selections, voucher codes, and payment details. * The resulting array can be used for fingerprint generation or draft persistence. * * @param BookingDto $bookingDto The booking DTO to extract data from * * @return array Serializable array of all user-editable data */ public function extractUserData(BookingDto $bookingDto): array { $data = [ 'paymentMethod' => $bookingDto->paymentMethod, 'bankAccount' => [ 'iban' => $bookingDto->bankAccount?->iban, 'accountHolder' => $bookingDto->bankAccount?->accountHolder, ], 'participants' => [], ]; foreach ($bookingDto->participants as $index => $participant) { $data['participants'][$index] = $this->extractParticipantData($participant); } return $data; } /** * Extracts user-editable data from a single participant. * * @param ParticipantDto $participant The participant to extract data from * * @return array Serializable array of participant data */ private function extractParticipantData(ParticipantDto $participant): array { return [ 'personalData' => [ 'firstName' => $participant->firstName, 'lastName' => $participant->lastName, 'dateOfBirth' => $participant->dateOfBirth?->format('Y-m-d'), 'email' => $participant->email, 'mobile' => $participant->mobile, 'gender' => $participant->gender, 'nationality' => $participant->nationality, ], 'address' => [ 'street' => $participant->address?->street, 'postCode' => $participant->address?->postCode, 'city' => $participant->address?->city, 'country' => $participant->address?->country, ], 'bodyDimensions' => [ 'height' => $participant->height, 'weight' => $participant->weight, 'shoeSize' => $participant->shoeSize, ], 'roomAssignment' => [ 'assignedRoomId' => $participant->assignedRoomId, 'remarksRoom' => $participant->remarksRoom, ], 'licensePlate' => $participant->licensePlate, 'services' => [ 'skiPass' => $participant->skiPass?->id, 'courses' => $this->normalizeServiceArray($participant->courses), 'board' => $this->normalizeServiceArray($participant->board), 'rentals' => $this->normalizeServiceArray($participant->rentals), 'rentalInsurance' => $participant->rentalInsurance?->id, 'additionalServices' => $this->normalizeServiceArray($participant->additionalServices), 'transportationOutbound' => $participant->transportationOutbound?->id, 'transportationInbound' => $participant->transportationInbound?->id, 'pickup' => $participant->pickup?->id, 'parking' => $participant->parking, 'insurance' => $participant->insurance?->id, 'bulkInsuranceBooking' => $participant->bulkInsuranceBooking, ], 'vouchers' => [ 'purchaseVoucherCode' => $participant->purchaseVoucherCode, 'promoVoucherCode' => $participant->promoVoucherCode, ], ]; } /** * Normalizes a service array to ensure consistent fingerprinting. * * Extracts service IDs, sorts them, and returns a simple indexed array. * This ensures that associative arrays, indexed arrays, and different orders * all produce the same fingerprint as long as the same services are present. * * @param array $services Array of Service objects * * @return array Sorted array of service IDs */ private function normalizeServiceArray(array $services): array { $ids = array_map(fn ($s) => $s->id, $services); sort($ids); return array_values($ids); } /** * Checks if the booking has unsaved changes in edit mode. * * Compares the current state fingerprint with the original fingerprint * that was set when the booking was loaded from the API. */ public function isDirty(BookingDto $bookingDto): bool { if (BookingDto::MODE_EDIT !== $bookingDto->getMode()) { return false; } if (null === $bookingDto->originalFingerprint) { return false; } $currentFingerprint = $this->generateFingerprint($bookingDto); return $bookingDto->originalFingerprint !== $currentFingerprint; } }