173 lines
7.1 KiB
PHP
173 lines
7.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Form\Model\BookingDto;
|
|
|
|
/**
|
|
* Generates fingerprints of booking state for change detection in edit mode.
|
|
*
|
|
* Creates SHA-256 hashes of all mutable booking data to detect unsaved changes.
|
|
* Used by EditController to determine if user modifications need to be saved.
|
|
*/
|
|
class BookingFingerprintService
|
|
{
|
|
/**
|
|
* Generates a fingerprint (hash) of all mutable booking data.
|
|
*
|
|
* The fingerprint includes payment details and all participant data including
|
|
* personal information, addresses, body dimensions, room assignments, and service selections.
|
|
*/
|
|
public function generateFingerprint(BookingDto $bookingDto, bool $logData = false): string
|
|
{
|
|
$data = [
|
|
'paymentMethod' => $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)));
|
|
}
|
|
}
|
|
}
|