wip: booking process, refactor participant room assignment logic

This commit is contained in:
Björn Fromme
2025-07-22 08:52:13 +02:00
parent b3c633dccb
commit 2f5b855bae
16 changed files with 216 additions and 94 deletions
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
namespace App\Form\ChoiceLoader;
use App\Form\DataAdapters\ParticipantDataAdapter;
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
/**
* Choice loader for participant room assignments.
*
* Generates room choices for each participant based on availability,
* current assignments, and room capacity constraints. This loader
* ensures participants can only select rooms with available capacity
* while maintaining their current assignment if applicable.
*/
class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
{
private ?ChoiceListInterface $choiceList = null;
public function __construct(
private readonly ChoiceListFactoryInterface $factory,
private readonly ParticipantDataAdapter $adapter,
private readonly array $selectedRooms,
private readonly int $participantIndex,
) {
}
public function loadChoiceList(?callable $value = null): ChoiceListInterface
{
if (null === $this->choiceList) {
$choices = $this->generateRoomChoicesForParticipant();
$this->choiceList = $this->factory->createListFromChoices($choices, $value);
}
return $this->choiceList;
}
public function loadChoicesForValues(array $values, ?callable $value = null): array
{
if (empty($values)) {
return [];
}
return $this->loadChoiceList($value)->getChoicesForValues($values);
}
public function loadValuesForChoices(array $choices, ?callable $value = null): array
{
if (empty($choices)) {
return [];
}
return $this->loadChoiceList($value)->getValuesForChoices($choices);
}
/**
* Generates room choices for the specific participant.
*
* @return array<string, int>
*/
private function generateRoomChoicesForParticipant(): array
{
$roomOccupancy = $this->calculateRoomOccupancy();
$participantRoomChoices = [];
$assignedRoomId = $this->getParticipantAssignedRoomId();
foreach ($this->selectedRooms as $roomSelection) {
$currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0;
// If this participant is already assigned to this room, exclude them from occupancy count
$adjustedOccupancy = $currentOccupancy;
if ($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 || $assignedRoomId === $roomSelection->roomId) {
$participantRoomChoices[$roomSelection->roomLabel] = $roomSelection->roomId;
}
}
return $participantRoomChoices;
}
/**
* Calculates room occupancy based on participant assignments.
*
* @return array<int, int>
*/
private function calculateRoomOccupancy(): array
{
$roomOccupancy = [];
foreach ($this->adapter->getParticipants() as $participant) {
$assignedRoomId = $this->adapter->getAssignedRoomId($participant);
if (null !== $assignedRoomId) {
$roomOccupancy[$assignedRoomId] = ($roomOccupancy[$assignedRoomId] ?? 0) + 1;
}
}
return $roomOccupancy;
}
/**
* Gets the assigned room ID for the current participant.
*/
private function getParticipantAssignedRoomId(): ?int
{
$participants = $this->adapter->getParticipants();
$participant = $participants[$this->participantIndex] ?? null;
return null !== $participant ? $this->adapter->getAssignedRoomId($participant) : null;
}
}