$bookingDto->paymentMethod, 'bankAccount' => [ 'iban' => $bookingDto->bankAccount?->iban, 'accountHolder' => $bookingDto->bankAccount?->accountHolder, ], 'participants' => [], ]; foreach ($bookingDto->participants as $index => $participant) { $data['participants'][$index] = [ '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, ], ]; } $fingerprint = hash('sha256', serialize($data)); if ($logData) { error_log(sprintf('[Fingerprint] Generated fingerprint: %s', $fingerprint)); error_log(sprintf('[Fingerprint] Serialized data: %s', serialize($data))); } return $fingerprint; } /** * 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); $isDirty = $bookingDto->originalFingerprint !== $currentFingerprint; // Debug logging to identify what changed if ($isDirty) { error_log(sprintf('[Fingerprint] DIRTY DETECTED! Original: %s, Current: %s', $bookingDto->originalFingerprint, $currentFingerprint)); $this->logFingerprintDiff($bookingDto); } return $isDirty; } /** * Logs detailed fingerprint data for debugging dirty state issues. */ private function logFingerprintDiff(BookingDto $bookingDto): void { foreach ($bookingDto->participants as $index => $participant) { $participantData = [ '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, ], '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, ], ]; error_log(sprintf('[Fingerprint] Participant %d data: %s', $index, json_encode($participantData))); } } }