sessionManager->getOrFail($request); } catch (AccommodationSessionNotFoundException $e) { return $this->createFailureResponse($e, false); } if ($redirect = $this->validateStepAccess($dto, 2)) { return $redirect; } [$ctx, $form] = $this->buildStep2Context($request, $dto, true); if ($form->isSubmitted() && $form->isValid()) { $dto->currentStep = 3; $this->sessionManager->save($request, $dto); return $this->redirectToRoute('app_groups_booking_step_3'); } return $this->render('groups/booking/step_2.html.twig', [ 'dto' => $dto, 'ctx' => $ctx, 'form' => $form, ]); } #[Route('/groups/booking/step-2/refresh', name: 'app_groups_booking_step_2_refresh', methods: ['POST'])] public function refresh(Request $request): Response { try { $dto = $this->sessionManager->getOrFail($request); } catch (AccommodationSessionNotFoundException $e) { return $this->createFailureResponse($e, true); } [$ctx, $form] = $this->buildStep2Context($request, $dto, false); return $this->htmxOobResponse( 'groups/booking/step_2.html.twig', ['booking_form', 'accommodation_summary'], ['dto' => $dto, 'ctx' => $ctx, 'form' => $form->createView()], ); } /** * @return array{AccommodationBookingContext, FormInterface} */ private function buildStep2Context(Request $request, AccommodationBookingDto $dto, bool $validate): array { $accommodation = $this->bookingService->loadAccommodation($dto->accommodationId); if (null === $accommodation) { throw $this->createNotFoundException(); } $prices = $this->bookingService->loadPrices($dto, $accommodation); $services = $this->bookingService->loadAvailableServices($dto, $accommodation); $form = $this->createForm(AccommodationStep2Type::class, $dto, [ 'board_service_choices' => $this->buildServiceChoices($services['boardServices']), 'additional_service_choices' => $this->buildServiceChoices($services['additionalServices']), 'max_adolescent_age' => (int) $accommodation->getMaxAdolescentAge(), 'validation_groups' => $validate ? ['step_2'] : false, ]); $form->handleRequest($request); $status = $this->bookingService->computeInquiryStatus($dto, $prices); $dto->isInquiry = $status->isInquiry; $dto->inquiryReasons = $status->reasons; $boardService = $this->resolveSelectedBoardService($dto, $services['boardServices']); $selectedAdditionalServices = $this->resolveSelectedAdditionalServices($dto, $services['additionalServices']); $priceBreakdown = null; if ($dto->dateFrom !== null && $dto->dateTo !== null) { $priceBreakdown = $this->priceCalculator->calculate( $dto->paxCount, $dto->minorsCount, $dto->getNights(), $dto->dateFrom, $dto->dateTo, $prices, $boardService, $selectedAdditionalServices, $accommodation->getCurrency() ?? 'EUR', ); $dto->totalPrice = $priceBreakdown['total']; $dto->priceBreakdown = $priceBreakdown; } $this->sessionManager->save($request, $dto); $ctx = new AccommodationBookingContext( accommodation: $accommodation, hotelCmsData: $this->bookingService->loadHotelCmsData($accommodation), boardServices: $services['boardServices'], additionalServices: $services['additionalServices'], groupedAdditionalServices: $services['groupedAdditionalServices'], ungroupedAdditionalServices: $services['ungroupedAdditionalServices'], priceBreakdown: $priceBreakdown, ); return [$ctx, $form]; } /** * @param BoardService[] $boardServices */ private function resolveSelectedBoardService(AccommodationBookingDto $dto, array $boardServices): ?BoardService { if (null === $dto->selectedBoardServiceId) { return null; } foreach ($boardServices as $service) { if ($service->getId() === $dto->selectedBoardServiceId) { return $service; } } return null; } /** * @param AdditionalService[] $additionalServices * * @return AdditionalService[] */ private function resolveSelectedAdditionalServices(AccommodationBookingDto $dto, array $additionalServices): array { if (empty($dto->selectedAdditionalServiceIds)) { return []; } $selectedIds = array_flip($dto->selectedAdditionalServiceIds); return array_values(array_filter( $additionalServices, fn(AdditionalService $s) => isset($selectedIds[$s->getId()]), )); } /** * Builds an ID-keyed choices array for Symfony form binding. * Labels and price formatting are handled in Twig. * * @param BoardService[]|AdditionalService[] $services * * @return array */ private function buildServiceChoices(array $services): array { $choices = []; foreach ($services as $service) { $choices[(string) $service->getId()] = $service->getId(); } return $choices; } }