feat: improved error handling and redirect on invalid session
This commit is contained in:
@@ -138,7 +138,8 @@ class IndexController extends AbstractController
|
||||
return $this->redirectToRoute('app_account');
|
||||
}
|
||||
|
||||
return $this->redirect($returnUrl);
|
||||
// Use htmxRedirect for cross-origin safety (returnUrl may be external)
|
||||
return $this->htmxRedirect($request, $returnUrl);
|
||||
}
|
||||
|
||||
return $this->render('booking/modal_cancel.html.twig');
|
||||
|
||||
@@ -108,6 +108,7 @@ class Step2Controller extends AbstractController
|
||||
'bookingDto' => $bookingCreateDto,
|
||||
'cardsData' => $cardsData,
|
||||
'summaryData' => $summaryData,
|
||||
'isSubmitted' => $form->isSubmitted(),
|
||||
];
|
||||
|
||||
return $this->render('booking/create/step_2.html.twig', $templateData);
|
||||
@@ -123,7 +124,11 @@ class Step2Controller extends AbstractController
|
||||
)]
|
||||
public function editParticipant(int $index, Request $request): Response
|
||||
{
|
||||
$bookingDto = $this->loadBookingDtoOrFail($request, BookingDto::MODE_CREATE);
|
||||
$result = $this->loadBookingDtoOrRedirect($request, BookingDto::MODE_CREATE);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
$bookingDto = $result;
|
||||
|
||||
// Validate participant index
|
||||
if (false === isset($bookingDto->participants[$index])) {
|
||||
@@ -178,7 +183,11 @@ class Step2Controller extends AbstractController
|
||||
)]
|
||||
public function refreshParticipantForm(int $index, Request $request): Response
|
||||
{
|
||||
$bookingDto = $this->loadBookingDtoOrFail($request, BookingDto::MODE_CREATE);
|
||||
$result = $this->loadBookingDtoOrRedirect($request, BookingDto::MODE_CREATE);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
}
|
||||
$bookingDto = $result;
|
||||
|
||||
// Validate participant index
|
||||
if (false === isset($bookingDto->participants[$index])) {
|
||||
|
||||
@@ -34,7 +34,7 @@ trait BookingExceptionHandlerTrait
|
||||
try {
|
||||
return $bookingService->getOrCreateBookingCreateDto($request);
|
||||
} catch (BookingSessionNotFoundException $e) {
|
||||
$this->addFlash('error', 'Ihre Buchungssitzung ist abgelaufen. Bitte starten Sie eine neue Buchung.');
|
||||
$this->addFlash('error', 'Deine Buchungssitzung ist abgelaufen. Bitte starte eine neue Buchung.');
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_error');
|
||||
} catch (TravelNotFoundException $e) {
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Form\BookingParticipantType;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantEditDto;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -20,16 +21,18 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
trait ParticipantCardFlowTrait
|
||||
{
|
||||
/**
|
||||
* Load BookingDto from session or throw exception.
|
||||
* Load BookingDto from session or return redirect to login.
|
||||
*
|
||||
* @throws \RuntimeException When booking data not found in session
|
||||
* Returns RedirectResponse when session data is missing (e.g., after cancel or session expiry).
|
||||
*/
|
||||
private function loadBookingDtoOrFail(Request $request, string $mode): BookingDto
|
||||
private function loadBookingDtoOrRedirect(Request $request, string $mode): BookingDto|RedirectResponse
|
||||
{
|
||||
$bookingDto = $this->bookingService->getBookingDto($request, $mode);
|
||||
|
||||
if (null === $bookingDto) {
|
||||
throw new \RuntimeException(sprintf('Booking data not found in session for mode: %s', $mode));
|
||||
$this->addFlash('info', 'Deine Sitzung ist abgelaufen. Bitte starte eine neue Buchung.');
|
||||
|
||||
return $this->redirectToRoute('app_login');
|
||||
}
|
||||
|
||||
return $bookingDto;
|
||||
@@ -183,4 +186,6 @@ trait ParticipantCardFlowTrait
|
||||
abstract private function render(string $view, array $parameters = [], ?Response $response = null): Response;
|
||||
|
||||
abstract protected function addFlash(string $type, mixed $message): void;
|
||||
|
||||
abstract protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse;
|
||||
}
|
||||
|
||||
+21
-1
@@ -4,10 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Htmx;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Trait providing HTMX Out-of-Band swap functionality for controllers.
|
||||
* Trait providing HTMX functionality for controllers.
|
||||
*/
|
||||
trait HxTrait
|
||||
{
|
||||
@@ -46,4 +47,23 @@ trait HxTrait
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a redirect response that works correctly with HTMX requests.
|
||||
*
|
||||
* For HTMX requests, returns a 200 response with HX-Redirect header,
|
||||
* which triggers a full page navigation (avoiding CORS issues with cross-origin redirects).
|
||||
* For regular requests, returns a standard HTTP redirect.
|
||||
*/
|
||||
protected function htmxRedirect(Request $request, string $url): Response
|
||||
{
|
||||
if ($request->headers->has('HX-Request')) {
|
||||
$response = new Response('', Response::HTTP_OK);
|
||||
$response->headers->set('HX-Redirect', $url);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
return $this->redirect($url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
messages: errorMessages
|
||||
} %}
|
||||
|
||||
<a href="{{ returnUrl }}" class="button button--secondary">
|
||||
{# hx-boost disabled for cross-origin #}
|
||||
<a href="{{ returnUrl }}" hx-boost="false" class="button button--secondary">
|
||||
Zurück
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
{# Guest user - show simple homepage link #}
|
||||
<a href="{{ returnUrl }}" class="button button--primary">
|
||||
{# Guest user - show simple homepage link (hx-boost disabled for cross-origin) #}
|
||||
<a href="{{ returnUrl }}" hx-boost="false" class="button button--primary">
|
||||
Zurück
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user