wip: backport booking create flow form handling to edit flow

This commit is contained in:
Björn Fromme
2026-03-16 11:59:10 +01:00
parent e330626b03
commit d70a27f2fd
5 changed files with 143 additions and 32 deletions
+4
View File
@@ -60,6 +60,10 @@ class BookingEditDto implements BookingDtoInterface
$pickup = $booking->getPickupForParticipant($index);
$participantData->pickup = $pickup;
// Room assignment - extract from booking room mappings
$room = $booking->getRoomForParticipant($index);
$participantData->assignedRoomId = $room?->id;
$instance->participants[$index] = $participantData;
}
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Form\Service\Factory;
use App\BusProNet\Model\Room;
use App\Form\ChoiceLoader\ParticipantRoomChoiceLoader;
use App\Form\Model\ParticipantDto;
use App\Form\Model\RoomSelectionDto;
@@ -11,6 +12,9 @@ use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
/**
* Factory service for creating ParticipantRoomChoiceLoader instances.
*
* Provides factory methods for both create and edit contexts, converting
* different room data formats into a unified choice loader interface.
*/
class ParticipantRoomChoiceLoaderFactory
{
@@ -20,12 +24,16 @@ class ParticipantRoomChoiceLoaderFactory
}
/**
* Creates a ParticipantRoomChoiceLoader for the given participant and room data.
* Creates a ParticipantRoomChoiceLoader for create context.
*
* @param ParticipantDto[] $allParticipants
* @param RoomSelectionDto[] $selectedRooms
* Uses room selections from step 1 (RoomSelectionDto objects) to generate
* available room choices for participants.
*
* @param ParticipantDto[] $allParticipants all participants from the booking
* @param RoomSelectionDto[] $selectedRooms rooms selected in create flow step 1
* @param int $participantIndex index of the participant needing room choices
*/
public function create(
public function createForCreate(
array $allParticipants,
array $selectedRooms,
int $participantIndex,
@@ -37,4 +45,62 @@ class ParticipantRoomChoiceLoaderFactory
$participantIndex
);
}
/**
* Creates a ParticipantRoomChoiceLoader for edit context.
*
* Uses already-booked rooms from the existing booking to generate available
* room choices. Participants can only be reassigned within the room types
* that have already been booked.
*
* @param ParticipantDto[] $allParticipants all participants from the booking
* @param Room[] $bookedRooms rooms from the existing booking
* @param int $participantIndex index of the participant needing room choices
*/
public function createForEdit(
array $allParticipants,
array $bookedRooms,
int $participantIndex,
): ParticipantRoomChoiceLoader {
// Convert Booking Room objects to RoomSelectionDto format for the choice loader
$roomSelections = $this->convertBookedRoomsToSelections($bookedRooms);
return new ParticipantRoomChoiceLoader(
$this->choiceListFactory,
$allParticipants,
$roomSelections,
$participantIndex
);
}
/**
* Converts booked Room objects to RoomSelectionDto format.
*
* This adapter method allows reusing the existing ParticipantRoomChoiceLoader
* logic for edit context by converting the Booking->rooms array into the
* same format used in the create flow.
*
* @param Room[] $bookedRooms rooms from existing booking
*
* @return RoomSelectionDto[] converted room selections
*/
private function convertBookedRoomsToSelections(array $bookedRooms): array
{
$selections = [];
foreach ($bookedRooms as $room) {
$selection = new RoomSelectionDto();
$selection->roomId = $room->id;
$selection->roomLabel = $room->label;
$selection->roomPrice = $room->price;
// In edit context, totalCount represents how many of this room type were booked
$selection->quantity = $room->totalCount ?? 1;
// maxPax is the capacity per room
$selection->capacity = $room->maxPax ?? 1;
$selections[] = $selection;
}
return $selections;
}
}
@@ -10,6 +10,7 @@ use App\BusProNet\Model\Service;
use App\BusProNet\Utility\DirectionMapper;
use App\Form\Model\BookingCreateDto;
use App\Form\Model\BookingDtoInterface;
use App\Form\Model\BookingEditDto;
use App\Form\Service\Abstract\AbstractFieldOptionsProvider;
use App\Form\Service\Factory\ParticipantRoomChoiceLoaderFactory;
use App\Service\InsuranceMatchingService;
@@ -80,25 +81,44 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
*/
protected function registerFieldOptionProviders(): void
{
// Room assignment field provider (only available for create workflow)
$this->fieldOptionProviders['assignedRoomId'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
'label' => 'Zimmer',
'placeholder' => 'Nicht zugeordnet',
// Use factory to create context-aware choice loader that:
// - Shows only available rooms for this participant
// - Excludes rooms already assigned to other participants
// - Respects room capacity and booking constraints
'choice_loader' => $bookingDto instanceof BookingCreateDto
? $this->roomChoiceLoaderFactory->create(
// Room assignment field provider (available for both create and edit workflows)
$this->fieldOptionProviders['assignedRoomId'] = function (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) {
$choiceLoader = null;
$disabled = false;
if ($bookingDto instanceof BookingCreateDto) {
// Create context: use selected rooms from step 1
$choiceLoader = $this->roomChoiceLoaderFactory->createForCreate(
$bookingDto->participants,
$bookingDto->getSelectedRooms(),
$participantIndex
)
: null,
// Make field read-only when only one room type is selected
// Room is auto-assigned, no user choice needed
'disabled' => $bookingDto instanceof BookingCreateDto && 1 === count($bookingDto->getSelectedRooms()),
];
);
// Disable when only one room type selected (auto-assigned)
$disabled = 1 === count($bookingDto->getSelectedRooms());
} elseif ($bookingDto instanceof BookingEditDto) {
// Edit context: use already-booked rooms from booking
$choiceLoader = $this->roomChoiceLoaderFactory->createForEdit(
$bookingDto->participants,
$bookingDto->booking->rooms,
$participantIndex
);
// Disable when only one room in booking (no reassignment needed)
$disabled = 1 === count($bookingDto->booking->rooms);
}
return [
'label' => 'Zimmer',
'placeholder' => 'Nicht zugeordnet',
// Use factory to create context-aware choice loader that:
// - Shows only available rooms for this participant
// - Excludes rooms already assigned to other participants
// - Respects room capacity and booking constraints
'choice_loader' => $choiceLoader,
// Make field read-only when only one room type is available
// Room is auto-assigned or cannot be changed, no user choice needed
'disabled' => $disabled,
];
};
// Courses field provider - provides age-appropriate courses from travel data
$this->fieldOptionProviders['courses'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
@@ -615,7 +635,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
});
}
/**
* Generates label for rental insurance checkbox including pricing information.
*/