feat: improved error handling in controllers, cleanup

This commit is contained in:
Björn Fromme
2025-09-26 09:00:40 +02:00
parent 391dd63ebf
commit 122df1a3f5
11 changed files with 261 additions and 119 deletions
+56 -45
View File
@@ -4,6 +4,7 @@ namespace App\Service;
use App\BusProNet\Model\Room;
use App\BusProNet\Model\Travel;
use App\Exception\BookingSessionNotFoundException;
use App\Exception\NoRoomsAvailableException;
use App\Form\Model\BookingCreateDto;
use App\Form\Model\RoomSelectionDto;
@@ -53,6 +54,19 @@ class BookingService
$request->getSession()->remove('booking_create_baseline_snapshot');
}
/**
* Retrieves the booking creation DTO from the session.
*
* This method enforces the secure booking flow by only returning existing
* session data. Users must go through the proper initialization flow via
* CreateInitController to create new booking sessions.
*
* @param Request $request The HTTP request containing session data
*
* @return BookingCreateDto The booking DTO from session
*
* @throws BookingSessionNotFoundException When no valid booking session exists
*/
public function getOrCreateBookingCreateDto(Request $request): BookingCreateDto
{
$bookingCreateDto = $request->getSession()->get(self::BOOKING_CREATE_KEY);
@@ -62,40 +76,19 @@ class BookingService
return $bookingCreateDto;
}
// Legacy support: Create a new DTO if date_id and hotel_id are provided
// This maintains backward compatibility for existing URLs with UID parameters
$dateId = $request->query->getInt('date_id');
$hotelId = $request->query->getInt('hotel_id');
if (0 === $dateId || 0 === $hotelId) {
throw new NotFoundHttpException('No booking session found. Please start a new booking.');
}
$travelData = $this->travelDataService->getTravelData($dateId, $hotelId);
if (null === $travelData) {
throw new NotFoundHttpException(sprintf('Travel data not found for date ID %d and hotel ID %d', $dateId, $hotelId));
}
$roomsIdsAndQuantities = $this->processRoomQuantities($request);
$availableRooms = $travelData->getAvailableRooms();
// Prevent booking flow entry when no rooms are available
if (empty($availableRooms)) {
throw new NoRoomsAvailableException($dateId, $hotelId);
}
$roomSelections = array_map(
fn (Room $room) => $this->createRoomSelection($room, $roomsIdsAndQuantities),
$availableRooms
);
$bookingCreateDto = new BookingCreateDto($travelData, $hotelId);
$bookingCreateDto->roomSelections = $roomSelections;
$this->saveBookingCreateDto($request, $bookingCreateDto);
return $bookingCreateDto;
// No session found - user must go through proper init flow
throw new BookingSessionNotFoundException();
}
/**
* Saves the booking creation DTO to the session.
*
* Persists the current booking state to the session for retrieval
* across multiple HTTP requests during the booking flow.
*
* @param Request $request The HTTP request with session
* @param BookingCreateDto $bookingCreateDto The booking DTO to persist
*/
public function saveBookingCreateDto(Request $request, BookingCreateDto $bookingCreateDto): void
{
$request->getSession()->set(self::BOOKING_CREATE_KEY, $bookingCreateDto);
@@ -150,6 +143,17 @@ class BookingService
return $bookingCreateDto;
}
/**
* Creates a room selection DTO from room data and quantities.
*
* Converts a Room model into a RoomSelectionDto with the specified quantity
* selection. Used during booking initialization to create selectable room options.
*
* @param Room $room The room model to convert
* @param array $roomsIdsAndQuantities Array of room ID to quantity mappings
*
* @return RoomSelectionDto The room selection DTO
*/
private function createRoomSelection(Room $room, array $roomsIdsAndQuantities): RoomSelectionDto
{
$selection = new RoomSelectionDto();
@@ -163,23 +167,16 @@ class BookingService
}
/**
* Extracts room IDs and quantities from the request data.
* Calculates the total number of participants based on room selections.
*
* Processes the 'rooms' parameter from the request to extract room IDs
* as keys and their corresponding quantities as integer values. Filters
* out empty values and converts all quantities to integers.
* Multiplies each room's minimum occupancy (minPax) by the selected quantity
* to determine the total number of participants required for the booking.
*
* @param Request $request The HTTP request containing room data
* @param array $roomSelections Array of RoomSelectionDto objects
* @param Travel $travelData Travel data containing room information
*
* @return array<int, int> Array with room IDs as keys and quantities as values
* @return int Total number of participants required
*/
public function processRoomQuantities(Request $request): array
{
$rooms = $request->request->all('rooms');
return array_map('intval', array_filter($rooms, 'strlen'));
}
public function getParticipantsCount(array $roomSelections, Travel $travelData): int
{
$participantsCount = 0;
@@ -279,6 +276,12 @@ class BookingService
/**
* Resets all participant room assignments in the DTO.
*
* Clears room assignments when room selections change to prevent
* invalid assignments. Called when users modify their room selections
* in step 1 to ensure participants are reassigned appropriately.
*
* @param BookingCreateDto $dto The booking DTO to reset assignments for
*/
public function resetParticipantAssignments(BookingCreateDto $dto): void
{
@@ -302,6 +305,14 @@ class BookingService
/**
* Checks if room selection has changed compared to a previous snapshot.
*
* Compares the current room selection state with a baseline snapshot
* to detect changes that would require participant reassignment.
*
* @param array $oldSnapshot The baseline room selection snapshot
* @param BookingCreateDto $newDto The current booking DTO
*
* @return bool True if room selections have changed, false otherwise
*/
public function hasRoomSelectionChanged(array $oldSnapshot, BookingCreateDto $newDto): bool
{