chore: further cleanup and refactor
This commit is contained in:
1 parent
5a0bf11c81
commit
80969d1da0
21 files changed
+606
-321
No files matched your search
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Service\BookingSessionManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Shared base controller for booking creation steps.
|
||||
*/
|
||||
abstract class AbstractBookingCreateController extends AbstractController
|
||||
{
|
||||
protected function loadBookingCreateDto(BookingSessionManager $bookingSessionService, Request $request): BookingDto
|
||||
{
|
||||
return $bookingSessionService->getOrCreateBookingCreateDto($request);
|
||||
}
|
||||
|
||||
protected function createBookingCreateFailureResponse(\Throwable $exception, bool $htmx): Response
|
||||
{
|
||||
if (true === $htmx) {
|
||||
return $this->createBookingCreateHtmxFailureResponse();
|
||||
}
|
||||
|
||||
return $this->createBookingCreateRedirectFailureResponse($exception);
|
||||
}
|
||||
|
||||
protected function validateStepAccess(BookingDto $bookingCreateDto, int $expectedStep): ?RedirectResponse
|
||||
{
|
||||
// Allow access to current step or any previous step
|
||||
if ($expectedStep > $bookingCreateDto->currentStep) {
|
||||
$this->addFlash('error', 'Bitte erst die vorherigen Schritte abschließen.');
|
||||
|
||||
return $this->redirectToCurrentStep($bookingCreateDto);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles API errors by logging and adding a flash message.
|
||||
*
|
||||
* @param array<string, mixed> $context
|
||||
*/
|
||||
protected function handleApiError(
|
||||
LoggerInterface $logger,
|
||||
string $logMessage,
|
||||
array $context,
|
||||
string $flashMessage,
|
||||
): void {
|
||||
$logger->error($logMessage, $context);
|
||||
$this->addFlash('error', $flashMessage);
|
||||
}
|
||||
|
||||
private function createBookingCreateRedirectFailureResponse(\Throwable $exception): Response
|
||||
{
|
||||
$message = match (true) {
|
||||
$exception instanceof BookingSessionNotFoundException => 'Deine Buchungssitzung ist abgelaufen. Bitte starte eine neue Buchung.',
|
||||
$exception instanceof TravelNotFoundException => 'Reisedaten sind nicht (mehr) verfügbar.',
|
||||
$exception instanceof HotelNotInTravelException => 'Das gewählte Hotel ist für diese Reise nicht verfügbar.',
|
||||
default => 'Die Buchung konnte nicht geladen werden.',
|
||||
};
|
||||
|
||||
$this->addFlash('error', $message);
|
||||
|
||||
return $this->redirectToRoute('app_booking_create_error');
|
||||
}
|
||||
|
||||
private function createBookingCreateHtmxFailureResponse(): Response
|
||||
{
|
||||
return new Response('', 400);
|
||||
}
|
||||
|
||||
private function redirectToCurrentStep(BookingDto $bookingCreateDto): RedirectResponse
|
||||
{
|
||||
$route = match ($bookingCreateDto->currentStep) {
|
||||
2 => 'app_booking_create_step_2',
|
||||
3 => 'app_booking_create_step_3',
|
||||
4 => 'app_booking_create_step_4',
|
||||
default => 'app_booking_create_step_1',
|
||||
};
|
||||
|
||||
return $this->redirectToRoute($route);
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep1Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -13,7 +14,6 @@ use App\Service\BookingConfigurator;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\RoomPricingCalculator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -24,10 +24,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
* This controller manages room selection functionality where users
|
||||
* choose the types and quantities of rooms for their booking.
|
||||
*/
|
||||
class Step1Controller extends AbstractController
|
||||
class Step1Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -43,11 +41,11 @@ class Step1Controller extends AbstractController
|
||||
#[Route('/bookings/create/rooms', name: 'app_booking_create_step_1')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Get or create baseline snapshot for change detection
|
||||
$oldRoomSelectionSnapshot = $this->bookingSessionService->getOrCreateBaselineSnapshot($request, $bookingCreateDto);
|
||||
@@ -98,11 +96,11 @@ class Step1Controller extends AbstractController
|
||||
#[Route('/bookings/create/refresh', name: 'app_booking_create_step_1_refresh', methods: ['POST'])]
|
||||
public function refresh(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDtoForHtmx($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, true);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Process form data without validation to capture current state
|
||||
$form = $this->createForm(BookingCreateStep1Type::class, $bookingCreateDto, [
|
||||
|
||||
@@ -5,19 +5,20 @@ declare(strict_types=1);
|
||||
namespace App\Controller\Booking\Create;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Entity\User;
|
||||
use App\Form\BookingCreateStep2Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Service\BookingConfigurator;
|
||||
use App\Service\BookingCreateContextFactory;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\ParticipantDataPrefiller;
|
||||
use App\Service\RoomAssigner;
|
||||
use App\Service\TravelDataProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -28,11 +29,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
* This controller uses a card overview and lazy-loaded individual participant forms
|
||||
* for better performance and UX with large groups (50+ participants).
|
||||
*/
|
||||
class Step2Controller extends AbstractController
|
||||
class Step2Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly BookingConfigurator $bookingService,
|
||||
private readonly BookingSessionManager $bookingSessionService,
|
||||
@@ -49,15 +47,14 @@ class Step2Controller extends AbstractController
|
||||
#[Route('/bookings/create/participants', name: 'app_booking_create_step_2')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
// Load or create booking DTO
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 2)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 2)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
@@ -132,7 +129,9 @@ class Step2Controller extends AbstractController
|
||||
}
|
||||
}
|
||||
|
||||
/** @param array<int, \App\Form\Model\RoomSelectionDto> $roomSelections */
|
||||
/**
|
||||
* @param array<int, RoomSelectionDto> $roomSelections
|
||||
*/
|
||||
private function calculateParticipantsCount(array $roomSelections, Travel $travelData): int
|
||||
{
|
||||
$participantsCount = 0;
|
||||
|
||||
@@ -8,8 +8,9 @@ use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\TimeoutException;
|
||||
use App\BusProNet\Model\BookingResponse;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep3Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -20,7 +21,6 @@ use App\Service\BookingPriceMismatchAnalyzer;
|
||||
use App\Service\BookingSessionManager;
|
||||
use App\Service\RoomPricingCalculator;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -29,10 +29,8 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
/**
|
||||
* Handles the third step of the booking creation process (payment method selection).
|
||||
*/
|
||||
class Step3Controller extends AbstractController
|
||||
class Step3Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -52,14 +50,14 @@ class Step3Controller extends AbstractController
|
||||
#[Route('/bookings/create/payment', name: 'app_booking_create_step_3')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 3)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 3)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
@@ -203,11 +201,11 @@ class Step3Controller extends AbstractController
|
||||
#[Route('/bookings/create/payment/refresh', name: 'app_booking_create_step_3_refresh', methods: ['POST'])]
|
||||
public function refresh(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, true);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
$form = $this->createForm(BookingCreateStep3Type::class, $bookingCreateDto, [
|
||||
'validation_groups' => false,
|
||||
@@ -248,7 +246,7 @@ class Step3Controller extends AbstractController
|
||||
private function shouldFallbackToInquiryMode(BookingResponse $response): bool
|
||||
{
|
||||
// Check for "nicht möglich" status + inquiry suggestion in message
|
||||
if ('nicht möglich' !== $response->status) {
|
||||
if (BookingResponse::BOOKING_IMPOSSIBLE !== $response->status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@ namespace App\Controller\Booking\Create;
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\Exception\TimeoutException;
|
||||
use App\BusProNet\Model\Notification;
|
||||
use App\Controller\Booking\Traits\BookingCreateTrait;
|
||||
use App\Controller\Booking\Traits\BookingExceptionHandlerTrait;
|
||||
use App\Entity\User;
|
||||
use App\Exception\BookingSessionNotFoundException;
|
||||
use App\Exception\HotelNotInTravelException;
|
||||
use App\Exception\NewsletterProviderException;
|
||||
use App\Exception\TravelNotFoundException;
|
||||
use App\Form\BookingCreateStep4Type;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Htmx\HxTrait;
|
||||
@@ -20,7 +21,6 @@ use App\Service\BookingSessionManager;
|
||||
use App\Service\MailjetApiClient;
|
||||
use App\Service\NewsletterManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
@@ -30,10 +30,8 @@ use Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* Handles the fourth step of the booking creation process (confirmation).
|
||||
*/
|
||||
class Step4Controller extends AbstractController
|
||||
class Step4Controller extends AbstractBookingCreateController
|
||||
{
|
||||
use BookingCreateTrait;
|
||||
use BookingExceptionHandlerTrait;
|
||||
use HxTrait;
|
||||
|
||||
public function __construct(
|
||||
@@ -54,14 +52,14 @@ class Step4Controller extends AbstractController
|
||||
#[Route('/bookings/create/confirmation', name: 'app_booking_create_step_4')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$result = $this->getOrCreateBookingCreateDto($this->bookingSessionService, $request);
|
||||
if ($result instanceof Response) {
|
||||
return $result;
|
||||
try {
|
||||
$bookingCreateDto = $this->loadBookingCreateDto($this->bookingSessionService, $request);
|
||||
} catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $exception) {
|
||||
return $this->createBookingCreateFailureResponse($exception, false);
|
||||
}
|
||||
$bookingCreateDto = $result;
|
||||
|
||||
// Validate step access
|
||||
if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
|
||||
if (null !== $redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user