'\d+'] )] public function editParticipant(int $index, Request $request): Response { $result = $this->loadBookingDtoOrRedirect($request); if ($result instanceof Response) { return $result; } $bookingCreateDto = $result; $this->participantFormSupportService->ensureParticipantExists($bookingCreateDto, $index); if ($redirect = $this->redirectIfRoomSelectionsStale($request, $bookingCreateDto, $this->bookingSessionService)) { return $redirect; } $this->bookingService->updateBookingStatusFromRoomSelection($bookingCreateDto); $this->bookingSessionService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE); $form = $this->createParticipantForm($bookingCreateDto, $index); $form->handleRequest($request); $isSubmitted = $form->isSubmitted(); $isDummyDataFill = $this ->prepopulationService ->isDummyDataFillRequested($bookingCreateDto->participants[$index], $bookingCreateDto->getMode()) ; if (true === $isSubmitted && true === $isDummyDataFill) { $this->prepopulationService->fillDummyParticipant($bookingCreateDto->participants[$index], $index); $this->bookingService->preselectDefaultServices($bookingCreateDto); $this->bookingService->applyCreateBookingStatusRules($bookingCreateDto); $this->bookingSessionService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE); $form = $this->createParticipantForm($bookingCreateDto, $index); return $this->renderParticipantForm($form, $index, $bookingCreateDto); } if (true === $isSubmitted) { $this->bookingService->preselectDefaultServices($bookingCreateDto); $this->bookingService->applyCreateBookingStatusRules($bookingCreateDto); } $notifications = $this->participantFormSupportService->collectAndClearNotifications($bookingCreateDto); if (true === $isSubmitted && true === $form->isValid()) { $this->bookingSessionService->saveBookingDto($request, $bookingCreateDto, BookingDto::MODE_CREATE); $this->addNotificationsAsFlashMessages($notifications); return $this->redirectToRoute('app_booking_create_step_2'); } return $this->renderParticipantForm($form, $index, $bookingCreateDto); } /** * HTMX refresh endpoint for individual participant form. */ #[Route( path: '/bookings/create/participants/{index}/refresh', name: 'app_booking_create_step_2_participant_refresh', requirements: ['index' => '\d+'], methods: ['POST'] )] public function refreshParticipantForm(int $index, Request $request): Response { $result = $this->loadBookingDtoOrRedirect($request); if ($result instanceof Response) { return $result; } $bookingDto = $result; $this->participantFormSupportService->ensureParticipantExists($bookingDto, $index); if ($redirect = $this->redirectIfRoomSelectionsStale($request, $bookingDto, $this->bookingSessionService)) { return $redirect; } $this->bookingService->updateBookingStatusFromRoomSelection($bookingDto); return $this->handleParticipantRefresh( $request, $bookingDto, $index, 'app_booking_create_step_2_participant_refresh' ); } /** @param FormInterface $form */ private function renderParticipantForm( FormInterface $form, int $index, BookingDto $bookingDto, ): Response { $bookingCreateContext = $this->createContextFactory->create( $bookingDto, RoomPricingCalculator::PRICING_MODE_SELECTION ); return $this->render('booking/create/step_2_participant.html.twig', [ 'form' => $form, 'participantIndex' => $index, 'bookingCreateContext' => $bookingCreateContext, 'bookingDto' => $bookingCreateContext->bookingDto, 'summaryData' => $bookingCreateContext->summaryData, 'refreshRouteName' => 'app_booking_create_step_2_participant_refresh', ]); } private function loadBookingDtoOrRedirect(Request $request): BookingDto|Response { $bookingDto = $this->bookingSessionService->getBookingDto($request, BookingDto::MODE_CREATE); if (null === $bookingDto) { $this->addFlash('info', 'Deine Sitzung ist abgelaufen. Bitte starte eine neue Buchung.'); return $this->redirectToRoute('app_login'); } return $bookingDto; } /** @return FormInterface */ private function createParticipantForm(BookingDto $bookingDto, int $index): FormInterface { $wrapper = $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index); return $this->createForm( BookingParticipantType::class, $wrapper, $this->participantFormSupportService->getParticipantFormOptions($bookingDto) ); } private function handleParticipantRefresh( Request $request, BookingDto $bookingDto, int $index, string $refreshRouteName, ): Response { $form = $this->createForm( BookingParticipantType::class, $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index), $this->participantFormSupportService->getParticipantFormOptions($bookingDto, true) ); $form->handleRequest($request); $this->bookingService->preselectDefaultServices($bookingDto); if (BookingDto::MODE_CREATE === $bookingDto->getMode()) { $this->bookingService->applyCreateBookingStatusRules($bookingDto); } $form = $this->createForm( BookingParticipantType::class, $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index), $this->participantFormSupportService->getParticipantFormOptions($bookingDto, true) ); $notifications = $this->participantFormSupportService->collectAndClearNotifications($bookingDto); $this->bookingSessionService->saveBookingDto($request, $bookingDto, $bookingDto->getMode()); $bookingCreateContext = $this->createContextFactory->create( $bookingDto, RoomPricingCalculator::PRICING_MODE_SELECTION ); $response = $this->htmxOobResponse( 'booking/_participant_form.html.twig', ['participant_form', 'booking_summary'], [ 'form' => $form->createView(), 'participantIndex' => $index, 'bookingCreateContext' => $bookingCreateContext, 'bookingDto' => $bookingCreateContext->bookingDto, 'summaryData' => $bookingCreateContext->summaryData, 'refreshRouteName' => $refreshRouteName, ] ); if (false === empty($notifications)) { $response->headers->set('HX-Trigger', json_encode([ 'showNotifications' => $notifications, ])); } return $response; } /** * @param array $notifications */ private function addNotificationsAsFlashMessages(array $notifications): void { foreach ($notifications as $notification) { $this->addFlash($notification['type'], $notification['message']); } } }