wip: booking process, room assignment
This commit is contained in:
@@ -10,6 +10,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class BookingCreateParticipantType extends AbstractType
|
||||
@@ -52,6 +54,16 @@ class BookingCreateParticipantType extends AbstractType
|
||||
'label' => 'Telefon (mobil)',
|
||||
'required' => false,
|
||||
])
|
||||
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
|
||||
$form->add('assignedRoomId', ChoiceType::class, [
|
||||
'label' => 'Zimmer',
|
||||
'placeholder' => 'Bitte wählen',
|
||||
'choices' => $options['room_choices'][$data->index],
|
||||
]);
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
@@ -59,6 +71,7 @@ class BookingCreateParticipantType extends AbstractType
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ParticipantDto::class,
|
||||
'room_choices' => [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,123 @@ use App\Form\Model\BookingCreateDto;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class BookingCreateStep2Type extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('participants', CollectionType::class, [
|
||||
'entry_type' => BookingCreateParticipantType::class,
|
||||
'allow_add' => false,
|
||||
'allow_delete' => false,
|
||||
'by_reference' => false,
|
||||
]);
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit']);
|
||||
}
|
||||
|
||||
public function onPreSetData(FormEvent $event): void
|
||||
{
|
||||
/** @var BookingCreateDto $bookingCreateDto */
|
||||
$bookingCreateDto = $event->getData();
|
||||
$form = $event->getForm();
|
||||
|
||||
if (null === $bookingCreateDto) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count participants assigned to each room
|
||||
$roomOccupancy = [];
|
||||
foreach ($bookingCreateDto->participants as $participant) {
|
||||
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
|
||||
{
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
/** @var BookingCreateDto $bookingCreateDto */
|
||||
$bookingCreateDto = $form->getData();
|
||||
|
||||
$roomOccupancy = [];
|
||||
foreach ($data['participants'] as $participant) {
|
||||
if (null !== $participant['assignedRoomId']) {
|
||||
$assignedRoomId = (int) $participant['assignedRoomId'];
|
||||
$roomOccupancy[$assignedRoomId] = ($roomOccupancy[$assignedRoomId] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
$roomChoices = [];
|
||||
foreach ($data['participants'] as $index => $participant) {
|
||||
$participantRoomChoices = [];
|
||||
$assignedRoomId = (int) $participant['assignedRoomId'];
|
||||
|
||||
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 ($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;
|
||||
}
|
||||
}
|
||||
|
||||
$roomChoices[$index] = $participantRoomChoices;
|
||||
}
|
||||
$form->remove('participants');
|
||||
$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 configureOptions(OptionsResolver $resolver): void
|
||||
|
||||
@@ -4,16 +4,15 @@ namespace App\Form\Model;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
class BookingCreateDto
|
||||
{
|
||||
public int $currentStep = 1;
|
||||
|
||||
#[Assert\Valid]
|
||||
/**
|
||||
* @var array<int, RoomSelectionDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $roomSelections = [];
|
||||
|
||||
/**
|
||||
@@ -26,42 +25,9 @@ class BookingCreateDto
|
||||
{
|
||||
}
|
||||
|
||||
#[Assert\Callback(groups: ['booking_create_step_1'])]
|
||||
public function assertValidRoomSelections(ExecutionContextInterface $context): void
|
||||
{
|
||||
$participantsCount = $this->getParticipantsCount();
|
||||
$selectedContingent = 0;
|
||||
|
||||
foreach ($this->roomSelections as $roomSelection) {
|
||||
$selectedContingent += $roomSelection->quantity * $roomSelection->minPax;
|
||||
}
|
||||
|
||||
if (0 === $selectedContingent) {
|
||||
$context->buildViolation('Bitte mindestens ein Zimmer auswählen.')
|
||||
->atPath('roomSelections')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if ($selectedContingent !== $participantsCount) {
|
||||
$context->buildViolation('Die ausgewählten Zimmer passen nicht zur Teilnehmerzahl.')
|
||||
->atPath('roomSelections')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
public function getParticipantsCount(): int
|
||||
{
|
||||
$participantsCount = 0;
|
||||
$rooms = $this->travelData->getAvailableRooms();
|
||||
|
||||
foreach ($this->roomSelections as $roomSelection) {
|
||||
$room = $rooms[$roomSelection->roomId];
|
||||
$participantsCount += $room->minPax * $roomSelection->quantity;
|
||||
}
|
||||
|
||||
return $participantsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
public function getSelectedRooms(): array
|
||||
{
|
||||
return array_filter($this->roomSelections, function (RoomSelectionDto $roomSelection) {
|
||||
|
||||
@@ -37,6 +37,10 @@ class ParticipantDto
|
||||
public ?string $email = null;
|
||||
|
||||
public ?string $mobile = null;
|
||||
|
||||
#[Assert\NotNull(message: 'Bitte ein Zimmer auswählen', groups: ['booking_create_step_2'])]
|
||||
public ?int $assignedRoomId = null;
|
||||
|
||||
public array $courses = [];
|
||||
public array $additionalServices = [];
|
||||
public array $skiPass = [];
|
||||
|
||||
Reference in New Issue
Block a user