From c373a9895355d2093cacc882471c600b1319c6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Fri, 13 Feb 2026 20:21:42 +0100 Subject: [PATCH] feat: reduce session payload --- src/Form/Model/BookingDto.php | 58 ++++++++++++++++++++++++++++++++++ src/Service/BookingService.php | 47 ++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/Form/Model/BookingDto.php b/src/Form/Model/BookingDto.php index 5730cc2..eb38201 100644 --- a/src/Form/Model/BookingDto.php +++ b/src/Form/Model/BookingDto.php @@ -325,4 +325,62 @@ class BookingDto { return AgencyLoader::INTERNAL_AGENCY_CODE === $this->agencyCode; } + + /** + * Controls session serialization to exclude the heavy Travel object graph. + * + * Replaces the full Travel object with just its integer ID. The Booking's + * travelData reference is also removed since it points to the same object. + * BookingService::hydrate() restores the Travel from cache after session read. + * + * @return array + */ + public function __serialize(): array + { + $data = get_object_vars($this); + + // Replace the full Travel object graph with just its ID + $data['travel'] = $this->travel->id; + + // Clone Booking to avoid mutating the live object, then strip its + // travelData reference which points to the same heavy Travel graph + if (null !== $this->booking) { + $booking = clone $this->booking; + $booking->travelData = null; + $data['booking'] = $booking; + } + + return $data; + } + + /** + * Restores the DTO from session data with a minimal Travel placeholder. + * + * Creates a Travel object containing only the ID. BookingService::hydrate() + * replaces this with the full Travel from cache on every session read. + * + * @param array $data + */ + public function __unserialize(array $data): void + { + // Extract the travel ID before the property loop — 'travel' in the + // serialized data is an int, not a Travel object + $travelId = $data['travel']; + unset($data['travel']); + + // Skip keys that no longer exist as declared properties to avoid + // dynamic property creation (deprecated since PHP 8.2) + foreach ($data as $key => $value) { + if (false === property_exists($this, $key)) { + continue; + } + $this->$key = $value; + } + + // Create a skeleton Travel with only the ID; BookingService::hydrate() + // replaces this with the full object from cache + $travel = new Travel(); + $travel->id = $travelId; + $this->travel = $travel; + } } diff --git a/src/Service/BookingService.php b/src/Service/BookingService.php index c3f6684..aad23ff 100644 --- a/src/Service/BookingService.php +++ b/src/Service/BookingService.php @@ -1,5 +1,7 @@ getSession()->get(self::BOOKING_CREATE_KEY); + $bookingDto = $this->getBookingDto($request, BookingDto::MODE_CREATE); - // Return existing DTO from session if available - if (null !== $bookingCreateDto) { - return $bookingCreateDto; + if (null !== $bookingDto) { + return $bookingDto; } - // No session found - user must go through proper init flow throw new BookingSessionNotFoundException(); } @@ -105,12 +105,15 @@ class BookingService } /** - * Retrieves the booking DTO from the session. + * Retrieves the booking DTO from the session and restores the Travel object. + * + * After deserialization the DTO contains only a Travel skeleton with the ID. + * This method replaces it with the full Travel from cache via hydrate(). * * @param Request $request The HTTP request containing session data * @param string $mode The booking mode (create/edit) * - * @return BookingDto|null The booking DTO from session or null if not found + * @return BookingDto|null The booking DTO or null if not found */ public function getBookingDto(Request $request, string $mode): ?BookingDto { @@ -121,7 +124,10 @@ class BookingService return null; } - return $session->get($sessionKey); + $bookingDto = $session->get($sessionKey); + $this->hydrate($bookingDto); + + return $bookingDto; } /** @@ -148,6 +154,31 @@ class BookingService return BookingDto::MODE_EDIT === $mode ? self::BOOKING_EDIT_KEY : self::BOOKING_CREATE_KEY; } + /** + * Restores the full Travel object from cache after session deserialization. + * + * BookingDto::__serialize() replaces Travel with just its ID to keep session + * payloads small. This method fetches the complete Travel from the + * TravelDataService cache and sets it on both the DTO and the Booking reference. + */ + private function hydrate(BookingDto $bookingDto): void + { + $travel = $this->travelDataService->getTravelData( + $bookingDto->travel->id, + $bookingDto->hotelId + ); + + if (null === $travel) { + return; + } + + $bookingDto->travel = $travel; + + if (null !== $bookingDto->booking) { + $bookingDto->booking->travelData = $travel; + } + } + /** * Clears all booking-related session data. *