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->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');
|
return $this->render('booking/modal_cancel.html.twig');
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ class Step2Controller extends AbstractController
|
|||||||
'bookingDto' => $bookingCreateDto,
|
'bookingDto' => $bookingCreateDto,
|
||||||
'cardsData' => $cardsData,
|
'cardsData' => $cardsData,
|
||||||
'summaryData' => $summaryData,
|
'summaryData' => $summaryData,
|
||||||
|
'isSubmitted' => $form->isSubmitted(),
|
||||||
];
|
];
|
||||||
|
|
||||||
return $this->render('booking/create/step_2.html.twig', $templateData);
|
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
|
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
|
// Validate participant index
|
||||||
if (false === isset($bookingDto->participants[$index])) {
|
if (false === isset($bookingDto->participants[$index])) {
|
||||||
@@ -178,7 +183,11 @@ class Step2Controller extends AbstractController
|
|||||||
)]
|
)]
|
||||||
public function refreshParticipantForm(int $index, Request $request): Response
|
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
|
// Validate participant index
|
||||||
if (false === isset($bookingDto->participants[$index])) {
|
if (false === isset($bookingDto->participants[$index])) {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ trait BookingExceptionHandlerTrait
|
|||||||
try {
|
try {
|
||||||
return $bookingService->getOrCreateBookingCreateDto($request);
|
return $bookingService->getOrCreateBookingCreateDto($request);
|
||||||
} catch (BookingSessionNotFoundException $e) {
|
} 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');
|
return $this->redirectToRoute('app_booking_create_error');
|
||||||
} catch (TravelNotFoundException $e) {
|
} catch (TravelNotFoundException $e) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use App\Form\BookingParticipantType;
|
|||||||
use App\Form\Model\BookingDto;
|
use App\Form\Model\BookingDto;
|
||||||
use App\Form\Model\ParticipantEditDto;
|
use App\Form\Model\ParticipantEditDto;
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
@@ -20,16 +21,18 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
trait ParticipantCardFlowTrait
|
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);
|
$bookingDto = $this->bookingService->getBookingDto($request, $mode);
|
||||||
|
|
||||||
if (null === $bookingDto) {
|
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;
|
return $bookingDto;
|
||||||
@@ -183,4 +186,6 @@ trait ParticipantCardFlowTrait
|
|||||||
abstract private function render(string $view, array $parameters = [], ?Response $response = null): Response;
|
abstract private function render(string $view, array $parameters = [], ?Response $response = null): Response;
|
||||||
|
|
||||||
abstract protected function addFlash(string $type, mixed $message): void;
|
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;
|
namespace App\Htmx;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait providing HTMX Out-of-Band swap functionality for controllers.
|
* Trait providing HTMX functionality for controllers.
|
||||||
*/
|
*/
|
||||||
trait HxTrait
|
trait HxTrait
|
||||||
{
|
{
|
||||||
@@ -46,4 +47,23 @@ trait HxTrait
|
|||||||
|
|
||||||
return $response;
|
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
|
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
|
Zurück
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,8 +47,8 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
{# Guest user - show simple homepage link #}
|
{# Guest user - show simple homepage link (hx-boost disabled for cross-origin) #}
|
||||||
<a href="{{ returnUrl }}" class="button button--primary">
|
<a href="{{ returnUrl }}" hx-boost="false" class="button button--primary">
|
||||||
Zurück
|
Zurück
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user