wip: booking process, refactor participant room assignment logic

This commit is contained in:
Björn Fromme
2025-07-18 15:59:45 +02:00
parent 6feb8178b5
commit b4db0cb8f2
2 changed files with 83 additions and 52 deletions
+49 -52
View File
@@ -2,6 +2,7 @@
namespace App\Form; namespace App\Form;
use App\Form\DataAdapters\ParticipantDataAdapter;
use App\Form\Model\BookingCreateDto; use App\Form\Model\BookingCreateDto;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\CollectionType;
@@ -29,49 +30,9 @@ class BookingCreateStep2Type extends AbstractType
return; return;
} }
// Count participants assigned to each room $adapter = new ParticipantDataAdapter($bookingCreateDto->participants);
$roomOccupancy = []; $roomChoices = $this->generateRoomChoices($adapter, $bookingCreateDto->getSelectedRooms());
foreach ($bookingCreateDto->participants as $participant) { $this->addParticipantsField($form, $roomChoices);
if (null !== $participant->assignedRoomId) {
$roomOccupancy[$participant->assignedRoomId] = ($roomOccupancy[$participant->assignedRoomId] ?? 0) + 1;
}
}
// Generate per-participant room choices
$roomChoices = [];
foreach ($bookingCreateDto->participants as $index => $participant) {
$participantRoomChoices = [];
foreach ($bookingCreateDto->getSelectedRooms() as $roomSelection) {
$currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0;
// If this participant is already assigned to this room, exclude them from occupancy count
$adjustedOccupancy = $currentOccupancy;
if ($participant->assignedRoomId === $roomSelection->roomId) {
--$adjustedOccupancy;
}
$remainingCapacity = $roomSelection->minPax - $adjustedOccupancy;
// Include room if it has capacity OR if it's the participant's current assignment
if ($remainingCapacity > 0 || $participant->assignedRoomId === $roomSelection->roomId) {
$participantRoomChoices[$roomSelection->roomLabel] = $roomSelection->roomId;
}
}
$roomChoices[$index] = $participantRoomChoices;
}
// Create a custom form type that handles per-participant room choices
$form->add('participants', CollectionType::class, [
'entry_type' => BookingCreateParticipantType::class,
'allow_add' => false,
'allow_delete' => false,
'by_reference' => false,
'entry_options' => [
'room_choices' => $roomChoices,
],
]);
} }
public function onPreSubmit(FormEvent $event): void public function onPreSubmit(FormEvent $event): void
@@ -81,20 +42,46 @@ class BookingCreateStep2Type extends AbstractType
/** @var BookingCreateDto $bookingCreateDto */ /** @var BookingCreateDto $bookingCreateDto */
$bookingCreateDto = $form->getData(); $bookingCreateDto = $form->getData();
$adapter = new ParticipantDataAdapter($data['participants']);
$roomChoices = $this->generateRoomChoices($adapter, $bookingCreateDto->getSelectedRooms());
$form->remove('participants');
$this->addParticipantsField($form, $roomChoices);
}
/**
* Calculates room occupancy based on participant assignments.
*
* @return array<int, int>
*/
private function calculateRoomOccupancy(ParticipantDataAdapter $adapter): array
{
$roomOccupancy = []; $roomOccupancy = [];
foreach ($data['participants'] as $participant) {
if (null !== $participant['assignedRoomId']) { foreach ($adapter->getParticipants() as $participant) {
$assignedRoomId = (int) $participant['assignedRoomId']; $assignedRoomId = $adapter->getAssignedRoomId($participant);
if (null !== $assignedRoomId) {
$roomOccupancy[$assignedRoomId] = ($roomOccupancy[$assignedRoomId] ?? 0) + 1; $roomOccupancy[$assignedRoomId] = ($roomOccupancy[$assignedRoomId] ?? 0) + 1;
} }
} }
$roomChoices = []; return $roomOccupancy;
foreach ($data['participants'] as $index => $participant) { }
$participantRoomChoices = [];
$assignedRoomId = (int) $participant['assignedRoomId'];
foreach ($bookingCreateDto->getSelectedRooms() as $roomSelection) { /**
* Generates room choices for each participant based on availability and current assignments.
*
* @return array<int, array<string, int>>
*/
private function generateRoomChoices(ParticipantDataAdapter $adapter, array $selectedRooms): array
{
$roomOccupancy = $this->calculateRoomOccupancy($adapter);
$roomChoices = [];
foreach ($adapter->getParticipants() as $index => $participant) {
$participantRoomChoices = [];
$assignedRoomId = $adapter->getAssignedRoomId($participant);
foreach ($selectedRooms as $roomSelection) {
$currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0; $currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0;
// If this participant is already assigned to this room, exclude them from occupancy count // If this participant is already assigned to this room, exclude them from occupancy count
@@ -113,7 +100,17 @@ class BookingCreateStep2Type extends AbstractType
$roomChoices[$index] = $participantRoomChoices; $roomChoices[$index] = $participantRoomChoices;
} }
$form->remove('participants');
return $roomChoices;
}
/**
* Adds the participants collection field to the form with the given room choices.
*
* @param array<int, array<string, int>> $roomChoices
*/
private function addParticipantsField(FormInterface $form, array $roomChoices): void
{
$form->add('participants', CollectionType::class, [ $form->add('participants', CollectionType::class, [
'entry_type' => BookingCreateParticipantType::class, 'entry_type' => BookingCreateParticipantType::class,
'allow_add' => false, 'allow_add' => false,
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Form\DataAdapters;
/**
* Adapter pattern to unify access to participant data regardless of whether it comes from
* objects (during PRE_SET_DATA) or arrays (during PRE_SUBMIT).
*/
class ParticipantDataAdapter
{
private array $participants;
public function __construct(array $participants)
{
$this->participants = $participants;
}
public function getParticipants(): array
{
return $this->participants;
}
public function getAssignedRoomId(mixed $participant): ?int
{
if (is_array($participant)) {
$assignedRoomId = $participant['assignedRoomId'] ?? null;
return null !== $assignedRoomId ? (int) $assignedRoomId : null;
}
return $participant->assignedRoomId;
}
}