feat: add typed dto for cms data
This commit is contained in:
@@ -178,7 +178,7 @@ services:
|
||||
tags:
|
||||
- { name: monolog.processor }
|
||||
|
||||
App\Service\Newsletter\NewsletterDoubleOptInService:
|
||||
App\Service\NewsletterDoubleOptInService:
|
||||
arguments:
|
||||
$newsletterConfirmationTtlHours: '%newsletter_confirmation_ttl_hours%'
|
||||
|
||||
|
||||
@@ -138,6 +138,8 @@ class Step2ParticipantController extends AbstractController
|
||||
'form' => $form->createView(),
|
||||
'participantIndex' => $index,
|
||||
'bookingCreateContext' => $bookingCreateContext,
|
||||
'bookingDto' => $bookingCreateContext->bookingDto,
|
||||
'summaryData' => $bookingCreateContext->summaryData,
|
||||
'refreshRouteName' => 'app_booking_create_step_2_participant_refresh',
|
||||
]);
|
||||
}
|
||||
@@ -207,6 +209,8 @@ class Step2ParticipantController extends AbstractController
|
||||
'form' => $form->createView(),
|
||||
'participantIndex' => $index,
|
||||
'bookingCreateContext' => $bookingCreateContext,
|
||||
'bookingDto' => $bookingCreateContext->bookingDto,
|
||||
'summaryData' => $bookingCreateContext->summaryData,
|
||||
'refreshRouteName' => $refreshRouteName,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -102,6 +102,9 @@ class ParticipantController extends AbstractController
|
||||
'form' => $form->createView(),
|
||||
'participantIndex' => $index,
|
||||
'bookingEditContext' => $context,
|
||||
'bookingDto' => $context->bookingDto,
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['id' => $id, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
@@ -158,6 +161,9 @@ class ParticipantController extends AbstractController
|
||||
'form' => $form->createView(),
|
||||
'participantIndex' => $index,
|
||||
'bookingEditContext' => $context,
|
||||
'bookingDto' => $context->bookingDto,
|
||||
'summaryData' => $context->summaryData,
|
||||
'mutableData' => $context->mutableData,
|
||||
'refreshRouteName' => 'app_booking_edit_participant_refresh',
|
||||
'refreshRouteParams' => ['id' => $id, 'index' => $index],
|
||||
'cancelRouteName' => 'app_booking_edit',
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use App\Model\BookingSummaryCmsHotelDto;
|
||||
|
||||
/**
|
||||
* DTO containing all booking summary data for sidebar display.
|
||||
*/
|
||||
@@ -17,7 +19,7 @@ class BookingSummaryDto
|
||||
public readonly int $participantCount,
|
||||
public readonly BookingSummaryPricingDto $pricing,
|
||||
public readonly BookingSummaryVoucherDto $vouchers,
|
||||
public readonly ?array $cmsData,
|
||||
public readonly ?BookingSummaryCmsHotelDto $cmsData,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
/**
|
||||
* Typed hotel CMS payload for the booking summary.
|
||||
*/
|
||||
final readonly class BookingSummaryCmsHotelDto
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed>|null $images
|
||||
*/
|
||||
public function __construct(
|
||||
public ?string $name,
|
||||
public ?string $address,
|
||||
public ?array $images,
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingSummaryDto;
|
||||
use App\Form\Model\BookingSummaryPricingDto;
|
||||
use App\Form\Model\BookingSummaryVoucherDto;
|
||||
use App\Model\BookingSummaryCmsHotelDto;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
@@ -153,7 +154,7 @@ class BookingSummaryDataService
|
||||
* Data is cached for 1 hour. This method can be called early in the booking
|
||||
* flow to warm the cache.
|
||||
*/
|
||||
public function getCmsDataForProduct(?string $productCode, ?string $hotelCode): ?array
|
||||
public function getCmsDataForProduct(?string $productCode, ?string $hotelCode): ?BookingSummaryCmsHotelDto
|
||||
{
|
||||
if (null === $hotelCode) {
|
||||
return null;
|
||||
@@ -171,14 +172,11 @@ class BookingSummaryDataService
|
||||
// Fetch CMS images (nice to have)
|
||||
$images = $this->cmsDataService->getProductImages($productCode, $hotelCode);
|
||||
|
||||
// Return combined data structure compatible with templates
|
||||
return [
|
||||
'hotel' => [
|
||||
'name' => $baseHotel?->name,
|
||||
'address' => $this->formatHotelAddress($baseHotel),
|
||||
'images' => $images,
|
||||
],
|
||||
];
|
||||
return new BookingSummaryCmsHotelDto(
|
||||
name: $baseHotel?->name,
|
||||
address: $this->formatHotelAddress($baseHotel),
|
||||
images: $images,
|
||||
);
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Failed to fetch hotel display data', [
|
||||
|
||||
@@ -141,10 +141,10 @@
|
||||
{% endmacro %}
|
||||
|
||||
{# Shared by booking create and edit flows; callers should pass one of the flow contexts. #}
|
||||
{% set bookingFlowContext = bookingEditContext|default(bookingCreateContext|default(null)) %}
|
||||
{% set bookingDto = bookingFlowContext.bookingDto %}
|
||||
{% set summaryData = bookingFlowContext.summaryData %}
|
||||
{% set mutableData = bookingFlowContext.mutableData|default(null) %}
|
||||
{% set bookingFlowContext = bookingFlowContext|default(bookingEditContext|default(bookingCreateContext|default(null))) %}
|
||||
{% set bookingDto = bookingDto|default(bookingFlowContext ? bookingFlowContext.bookingDto : null) %}
|
||||
{% set summaryData = summaryData|default(bookingFlowContext ? bookingFlowContext.summaryData : null) %}
|
||||
{% set mutableData = mutableData|default(bookingFlowContext and bookingFlowContext.mutableData is defined ? bookingFlowContext.mutableData : null) %}
|
||||
|
||||
{# Standalone participant form view (replaces main content area) #}
|
||||
{% block participant_form %}
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% include 'booking/_summary_hotel.html.twig' %}
|
||||
{% include 'booking/_summary_hotel.html.twig' with {
|
||||
'summaryData': summaryData
|
||||
} %}
|
||||
|
||||
{# Mutability information (edit mode only) #}
|
||||
{% if bookingDto.mode == constant('App\\Form\\Model\\BookingDto::MODE_EDIT') and mutableData %}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{% if summaryData.cmsData.hotel.name %}
|
||||
{% if summaryData.cmsData and summaryData.cmsData.name %}
|
||||
<div class="grid grid-cols-3 gap-x-4 py-4 border-t border-primary-bg">
|
||||
{% if summaryData.cmsData.hotel.images.resized.l[0] is defined %}
|
||||
<img src="{{ summaryData.cmsData.hotel.images.resized.l[0].url }}"
|
||||
alt="{{ summaryData.cmsData.hotel.images.resized.l[0].alt }}"
|
||||
{% if summaryData.cmsData.images and summaryData.cmsData.images.resized.l[0] is defined %}
|
||||
<img src="{{ summaryData.cmsData.images.resized.l[0].url }}"
|
||||
alt="{{ summaryData.cmsData.images.resized.l[0].alt }}"
|
||||
class="block w-full h-auto">
|
||||
{% endif %}
|
||||
<div class="{{ summaryData.cmsData.hotel.images.resized.l[0] is defined ? 'col-span-2' : 'col-span-3' }}">
|
||||
<span class="block font-semibold uppercase">{{ summaryData.cmsData.hotel.name }}</span>
|
||||
{% if summaryData.cmsData.hotel.address %}
|
||||
{{ summaryData.cmsData.hotel.address | nl2br }}
|
||||
<div class="{{ summaryData.cmsData.images and summaryData.cmsData.images.resized.l[0] is defined ? 'col-span-2' : 'col-span-3' }}">
|
||||
<span class="block font-semibold uppercase">{{ summaryData.cmsData.name }}</span>
|
||||
{% if summaryData.cmsData.address %}
|
||||
{{ summaryData.cmsData.address | nl2br }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
</h1>
|
||||
|
||||
<div class="lg:flex lg:space-x-8 pb-8 mb-8 border-b border-primary-bg/40">
|
||||
{% if cmsData.hotel.images.resized.l[0] is defined %}
|
||||
<img src="{{ cmsData.hotel.images.resized.l[0].url }}"
|
||||
alt="{{ cmsData.hotel.images.resized.l[0].alt }}"
|
||||
{% if cmsData and cmsData.images and cmsData.images.resized.l[0] is defined %}
|
||||
<img src="{{ cmsData.images.resized.l[0].url }}"
|
||||
alt="{{ cmsData.images.resized.l[0].alt }}"
|
||||
class="block w-full max-w-64 h-auto mb-4">
|
||||
{% endif %}
|
||||
<div class="text-white max-w-96">
|
||||
@@ -22,11 +22,11 @@
|
||||
<div class="text-lg font-semibold uppercase">
|
||||
{{ travel_date_from | date('d.m.Y') }} - {{ travel_date_to | date('d.m.Y') }}
|
||||
</div>
|
||||
{% if cmsData.hotel.name is defined and cmsData.hotel.name %}
|
||||
<div class="mt-4 font-semibold">{{ cmsData.hotel.name }}</div>
|
||||
{% if cmsData and cmsData.name %}
|
||||
<div class="mt-4 font-semibold">{{ cmsData.name }}</div>
|
||||
{% endif %}
|
||||
{% if cmsData.hotel.address is defined and cmsData.hotel.address %}
|
||||
<div>{{ cmsData.hotel.address | nl2br }}</div>
|
||||
{% if cmsData and cmsData.address %}
|
||||
<div>{{ cmsData.address | nl2br }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
<div id="main-content" class="flex-1 overflow-y-auto px-4 lg:px-8 py-8">
|
||||
{# Flash messages - must be inside main-content for HTMX swap to display them #}
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
{% include 'booking/_participant_form.html.twig' %}
|
||||
{% include 'booking/_participant_form.html.twig' with {
|
||||
'bookingCreateContext': bookingCreateContext,
|
||||
'bookingDto': bookingCreateContext.bookingDto,
|
||||
'summaryData': bookingCreateContext.summaryData
|
||||
} %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,9 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% include 'booking/_summary_hotel.html.twig' %}
|
||||
{% include 'booking/_summary_hotel.html.twig' with {
|
||||
'summaryData': bookingCreateContext.summaryData
|
||||
} %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,7 +125,9 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
{% include 'booking/_summary_hotel.html.twig' %}
|
||||
{% include 'booking/_summary_hotel.html.twig' with {
|
||||
'summaryData': bookingCreateContext.summaryData
|
||||
} %}
|
||||
</div>
|
||||
|
||||
{# Detailed Pricing Breakdown #}
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
{% include 'booking/_participant_form.html.twig' with {
|
||||
'bookingEditContext': bookingEditContext,
|
||||
'bookingDto': bookingEditContext.bookingDto,
|
||||
'summaryData': bookingEditContext.summaryData,
|
||||
'mutableData': bookingEditContext.mutableData|default(null),
|
||||
'participantIndex': participantIndex,
|
||||
'refreshRouteName': refreshRouteName,
|
||||
'refreshRouteParams': refreshRouteParams|default({}),
|
||||
|
||||
Reference in New Issue
Block a user