wip: modernized edit flow
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\ParticipantFieldHandlerRegistry;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
@@ -27,7 +27,7 @@ class BookingEditType extends AbstractType
|
||||
|
||||
public function onPreSetData(FormEvent $event): void
|
||||
{
|
||||
/** @var BookingEditDto $data */
|
||||
/** @var BookingDto $data */
|
||||
$data = $event->getData();
|
||||
$form = $event->getForm();
|
||||
|
||||
@@ -46,7 +46,7 @@ class BookingEditType extends AbstractType
|
||||
$form = $event->getForm();
|
||||
$submittedData = $event->getData();
|
||||
|
||||
/** @var BookingEditDto $bookingDto */
|
||||
/** @var BookingDto $bookingDto */
|
||||
$bookingDto = $form->getData();
|
||||
|
||||
// Process field handlers and synchronize submitted data with cleaned DTO state
|
||||
@@ -71,7 +71,7 @@ class BookingEditType extends AbstractType
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => BookingEditDto::class,
|
||||
'data_class' => BookingDto::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace App\Form;
|
||||
|
||||
use App\BusProNet\Form\CountryType;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\Contract\FieldOptionsProviderInterface;
|
||||
use App\Form\Service\Contract\FieldStateProviderInterface;
|
||||
@@ -14,6 +14,7 @@ use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -108,7 +109,7 @@ class BookingParticipantType extends AbstractType
|
||||
/**
|
||||
* Adds base fields to the form with field states applied.
|
||||
*/
|
||||
private function addBaseFields(FormInterface $form, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
private function addBaseFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Get field states for base fields
|
||||
$allFieldStates = $this->fieldStateProvider->getAllFieldStates($bookingDto, $participantIndex);
|
||||
@@ -151,12 +152,12 @@ class BookingParticipantType extends AbstractType
|
||||
], $getFieldState('email')))
|
||||
->add('mobile', TextType::class, $this->mergeFieldState([
|
||||
'label' => 'Telefon (mobil)',
|
||||
'required' => 0 === $participantIndex,
|
||||
'required' => false,
|
||||
'sanitize_html' => true,
|
||||
], $getFieldState('mobile')))
|
||||
->add('address', AddressType::class, $this->mergeFieldState([
|
||||
'label' => 'Adresse',
|
||||
'required' => 0 === $participantIndex,
|
||||
'required' => false,
|
||||
], $getFieldState('address')));
|
||||
|
||||
// Add body dimensions with state handling - use shouldIncludeField method
|
||||
@@ -172,11 +173,11 @@ class BookingParticipantType extends AbstractType
|
||||
* field states change based on submitted data.
|
||||
*
|
||||
* @param FormInterface $form The form to modify
|
||||
* @param BookingDtoInterface $bookingDto The booking data for context
|
||||
* @param BookingDto $bookingDto The booking data for context
|
||||
* @param int $participantIndex The participant index
|
||||
* @param array<string, mixed> $formData Submitted form data for state calculation
|
||||
*/
|
||||
private function removeExcludedFields(FormInterface $form, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): void
|
||||
private function removeExcludedFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex, array $formData = []): void
|
||||
{
|
||||
foreach (ParticipantDto::DYNAMIC_FIELDS as $fieldName) {
|
||||
if ($form->has($fieldName) && !$this->fieldStateProvider->shouldIncludeField($fieldName, $bookingDto, $participantIndex, $formData)) {
|
||||
@@ -188,7 +189,7 @@ class BookingParticipantType extends AbstractType
|
||||
/**
|
||||
* Rebuilds all fields with updated states based on submitted data.
|
||||
*/
|
||||
private function rebuildFieldsWithStates(FormInterface $form, BookingDtoInterface $bookingDto, int $participantIndex, array $submittedData): void
|
||||
private function rebuildFieldsWithStates(FormInterface $form, BookingDto $bookingDto, int $participantIndex, array $submittedData): void
|
||||
{
|
||||
// First, remove fields that should be excluded entirely
|
||||
$this->removeExcludedFields($form, $bookingDto, $participantIndex, $submittedData);
|
||||
@@ -220,7 +221,7 @@ class BookingParticipantType extends AbstractType
|
||||
/**
|
||||
* Adds all configured dynamic fields to the form with state conditions applied.
|
||||
*/
|
||||
private function addDynamicFields(FormInterface $form, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
private function addDynamicFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$dynamicFields = [
|
||||
'assignedRoomId' => ChoiceType::class,
|
||||
@@ -236,10 +237,14 @@ class BookingParticipantType extends AbstractType
|
||||
'pickup' => ChoiceType::class,
|
||||
'parking' => CheckboxType::class,
|
||||
'licensePlate' => TextType::class,
|
||||
'bulkInsuranceBooking' => CheckboxType::class,
|
||||
'insurance' => InsuranceChoiceType::class,
|
||||
];
|
||||
|
||||
// Insurance fields only available in create mode (API limitation)
|
||||
if (BookingDto::MODE_CREATE === $bookingDto->getMode()) {
|
||||
$dynamicFields['bulkInsuranceBooking'] = CheckboxType::class;
|
||||
$dynamicFields['insurance'] = InsuranceChoiceType::class;
|
||||
}
|
||||
|
||||
foreach ($dynamicFields as $fieldName => $fieldType) {
|
||||
if ($this->fieldOptionsProvider->hasFieldOptions($fieldName)) {
|
||||
// Check if field should be included in the form at all
|
||||
|
||||
@@ -33,6 +33,17 @@ class BankAccountDto
|
||||
#[Assert\IsTrue(message: 'Bitte akzeptieren Sie das SEPA-Mandat.')]
|
||||
public bool $sepaMandateAccepted = false;
|
||||
|
||||
public static function fromBankAccount(\App\BusProNet\Model\BankAccount $bankAccount): static
|
||||
{
|
||||
$instance = new static();
|
||||
$instance->iban = $bankAccount->iban;
|
||||
$instance->accountHolder = $bankAccount->holder;
|
||||
$instance->bankName = $bankAccount->bankName;
|
||||
$instance->sepaMandateAccepted = true;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns IBAN formatted with spaces for display (e.g., DE12 3456 7890 1234 5678 90).
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,11 @@ class BookingCreateDto implements BookingDtoInterface
|
||||
{
|
||||
}
|
||||
|
||||
public function getMode(): string
|
||||
{
|
||||
return BookingDtoInterface::MODE_CREATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Model;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Booking;
|
||||
use App\BusProNet\Model\Travel;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* Unified DTO for both booking creation and editing workflows.
|
||||
*
|
||||
* This DTO consolidates the previously separate BookingCreateDto and BookingEditDto
|
||||
* into a single class that handles both modes. All service selections are stored
|
||||
* in participant DTOs regardless of mode, ensuring consistent data structure and
|
||||
* simplifying pricing calculations, field handlers, and template rendering.
|
||||
*/
|
||||
class BookingDto
|
||||
{
|
||||
public const MODE_CREATE = 'create';
|
||||
public const MODE_EDIT = 'edit';
|
||||
|
||||
public int $currentStep = 1;
|
||||
|
||||
/**
|
||||
* @var array<int, RoomSelectionDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $roomSelections = [];
|
||||
|
||||
/**
|
||||
* @var array<int, ParticipantDto>
|
||||
*/
|
||||
#[Assert\Valid]
|
||||
public array $participants = [];
|
||||
|
||||
#[Assert\Choice(
|
||||
choices: [Constants::PAYMENT_METHOD_TRANSFER, Constants::PAYMENT_METHOD_DEBIT],
|
||||
message: 'Bitte wählen Sie eine gültige Zahlungsart.'
|
||||
)]
|
||||
public ?string $paymentMethod = Constants::PAYMENT_METHOD_TRANSFER;
|
||||
|
||||
public ?BankAccountDto $bankAccount = null;
|
||||
|
||||
public ?int $agencyId = null;
|
||||
|
||||
/**
|
||||
* Reference to booking entity (only populated in edit mode).
|
||||
*/
|
||||
public ?Booking $booking = null;
|
||||
|
||||
public function __construct(public Travel $travel, public int $hotelId)
|
||||
{
|
||||
}
|
||||
|
||||
public function getMode(): string
|
||||
{
|
||||
return null !== $this->booking ? self::MODE_EDIT : self::MODE_CREATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
public function getSelectedRooms(): array
|
||||
{
|
||||
// In edit mode, rooms are fixed - return empty array
|
||||
if (self::MODE_EDIT === $this->getMode()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_filter($this->roomSelections, function (RoomSelectionDto $roomSelection) {
|
||||
return 0 < $roomSelection->quantity;
|
||||
});
|
||||
}
|
||||
|
||||
public function getParticipants(): array
|
||||
{
|
||||
return $this->participants;
|
||||
}
|
||||
|
||||
public function hasParticipant(int $index): bool
|
||||
{
|
||||
return isset($this->participants[$index]);
|
||||
}
|
||||
|
||||
public function getParticipant(int $index): ?ParticipantDto
|
||||
{
|
||||
return $this->participants[$index] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a family booking based on participant age distribution.
|
||||
*
|
||||
* A family booking is defined as:
|
||||
* - 1 or 2 participants aged 18 or older (adults)
|
||||
* - At least 1 participant younger than 18 (children)
|
||||
*/
|
||||
public function isFamilyBooking(): bool
|
||||
{
|
||||
$adults = 0;
|
||||
$children = 0;
|
||||
|
||||
$travelStartDate = $this->travel->dateFrom;
|
||||
|
||||
foreach ($this->participants as $participant) {
|
||||
$age = $participant->getAge($travelStartDate);
|
||||
|
||||
if (null === $age) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($age >= 18) {
|
||||
++$adults;
|
||||
} else {
|
||||
++$children;
|
||||
}
|
||||
}
|
||||
|
||||
return ($adults >= 1 && $adults <= 2) && ($children >= 1);
|
||||
}
|
||||
|
||||
public function isCanceled(): bool
|
||||
{
|
||||
return null !== $this->booking && 'S' === $this->booking->status;
|
||||
}
|
||||
|
||||
public function isOption(): bool
|
||||
{
|
||||
return null !== $this->booking && 'O' === $this->booking->status;
|
||||
}
|
||||
|
||||
#[Assert\Callback(callback: 'validateRoomSelection', groups: ['booking_create_step_1'])]
|
||||
public function validateRoomSelection(ExecutionContextInterface $context): void
|
||||
{
|
||||
$selectedRooms = $this->getSelectedRooms();
|
||||
|
||||
if (0 === count($selectedRooms)) {
|
||||
$context->buildViolation('Bitte mindestens ein Zimmer/Bett auswählen')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validateBankAccount(ExecutionContextInterface $context): void
|
||||
{
|
||||
if (Constants::PAYMENT_METHOD_DEBIT !== $this->paymentMethod) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->bankAccount) {
|
||||
$context->buildViolation('Bitte geben Sie Ihre Bankverbindung an.')
|
||||
->atPath('bankAccount')
|
||||
->addViolation();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null === $this->bankAccount->iban || '' === trim($this->bankAccount->iban)) {
|
||||
$context->buildViolation('Bitte geben Sie Ihre IBAN ein.')
|
||||
->atPath('bankAccount.iban')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if (null === $this->bankAccount->accountHolder || '' === trim($this->bankAccount->accountHolder)) {
|
||||
$context->buildViolation('Bitte geben Sie den Kontoinhaber ein.')
|
||||
->atPath('bankAccount.accountHolder')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
if (false === $this->bankAccount->sepaMandateAccepted) {
|
||||
$context->buildViolation('Bitte akzeptieren Sie das SEPA-Mandat.')
|
||||
->atPath('bankAccount.sepaMandateAccepted')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,16 @@ namespace App\Form\Model;
|
||||
*/
|
||||
interface BookingDtoInterface
|
||||
{
|
||||
public const MODE_CREATE = 'create';
|
||||
public const MODE_EDIT = 'edit';
|
||||
|
||||
/**
|
||||
* Gets the booking mode (create or edit).
|
||||
*
|
||||
* @return string One of MODE_CREATE or MODE_EDIT constants
|
||||
*/
|
||||
public function getMode(): string;
|
||||
|
||||
/**
|
||||
* Gets all participants in the booking.
|
||||
*
|
||||
|
||||
@@ -21,6 +21,11 @@ class BookingEditDto implements BookingDtoInterface
|
||||
{
|
||||
}
|
||||
|
||||
public function getMode(): string
|
||||
{
|
||||
return BookingDtoInterface::MODE_EDIT;
|
||||
}
|
||||
|
||||
public static function fromBooking(Booking $booking, Travel $travel): static
|
||||
{
|
||||
$instance = new static($booking, $travel);
|
||||
@@ -30,7 +35,9 @@ class BookingEditDto implements BookingDtoInterface
|
||||
|
||||
foreach ($booking->participants as $index => $participant) {
|
||||
/** @var PersonalData $participant */
|
||||
$participantData = ParticipantDto::fromPersonalData($participant);
|
||||
// For the first participant (applicant), use applicant data instead of participant data
|
||||
$personalData = 0 === $index && null !== $booking->applicant ? $booking->applicant : $participant;
|
||||
$participantData = ParticipantDto::fromPersonalData($personalData);
|
||||
$participantData->index = $index;
|
||||
|
||||
$participantData->courses = $booking
|
||||
@@ -102,7 +109,10 @@ class BookingEditDto implements BookingDtoInterface
|
||||
/**
|
||||
* Gets all selected rooms for the booking (edit context).
|
||||
*
|
||||
* @return array<int, RoomSelectionDto> always returns an empty array for edit DTOs unless implemented
|
||||
* In edit mode, rooms are fixed and not selectable - returns empty array.
|
||||
* Participant count should be derived from actual participants, not room selections.
|
||||
*
|
||||
* @return array<int, RoomSelectionDto>
|
||||
*/
|
||||
public function getSelectedRooms(): array
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Validator\Constraints as AppAssert;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[AppAssert\Participant(groups: ['booking_edit', 'booking_create_step_2'])]
|
||||
#[AppAssert\ApplicantAddress(groups: ['booking_create_step_2'])]
|
||||
class ParticipantDto
|
||||
{
|
||||
/**
|
||||
@@ -132,6 +133,12 @@ class ParticipantDto
|
||||
$instance->weight = $personalData->weight;
|
||||
$instance->shoeSize = $personalData->shoeSize;
|
||||
|
||||
// Clone address to prevent shared object references that could cause mutations
|
||||
$instance->address = null !== $personalData->address ? clone $personalData->address : null;
|
||||
|
||||
$instance->remarksRoom = $personalData->remarksRoom;
|
||||
$instance->licensePlate = $personalData->licensePlate;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Abstract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldOptionsProviderInterface;
|
||||
|
||||
/**
|
||||
@@ -37,13 +37,13 @@ abstract class AbstractFieldOptionsProvider implements FieldOptionsProviderInter
|
||||
* static options defined in the form type.
|
||||
*
|
||||
* @param string $fieldName The name of the field to configure
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being configured
|
||||
* @param array $options Additional options to customize field behavior
|
||||
*
|
||||
* @return array<string, mixed> Symfony form field options, or empty array if field not supported
|
||||
*/
|
||||
public function getFieldOptions(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $options = []): array
|
||||
public function getFieldOptions(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $options = []): array
|
||||
{
|
||||
// Check if we have a provider for this field
|
||||
if (false === isset($this->fieldOptionProviders[$fieldName])) {
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Abstract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
use App\Form\Service\Contract\FieldStateProviderInterface;
|
||||
use App\Form\Service\Trait\FormTraversalTrait;
|
||||
@@ -36,13 +36,13 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
* condition independently to allow early field exclusion.
|
||||
*
|
||||
* @param string $fieldName The name of the field to evaluate
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the field should be included in the form, false if it should be excluded
|
||||
*/
|
||||
public function shouldIncludeField(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): bool
|
||||
public function shouldIncludeField(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): bool
|
||||
{
|
||||
if (false === isset($this->fieldStateConditions[$fieldName]['hidden'])) {
|
||||
return true; // No hidden condition means field should be included
|
||||
@@ -64,13 +64,13 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
* excluded from the form entirely rather than hidden with CSS.
|
||||
*
|
||||
* @param string $fieldName The name of the field to evaluate
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return array<string, mixed> Symfony form field options for state modifications
|
||||
*/
|
||||
public function getFieldState(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
|
||||
public function getFieldState(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): array
|
||||
{
|
||||
if (false === isset($this->fieldStateConditions[$fieldName])) {
|
||||
return [];
|
||||
@@ -139,13 +139,13 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
/**
|
||||
* Calculates field states for all configured fields at once.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return array<string, array<string, mixed>> Field states indexed by field name
|
||||
*/
|
||||
public function getAllFieldStates(BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
|
||||
public function getAllFieldStates(BookingDto $bookingDto, int $participantIndex, array $formData = []): array
|
||||
{
|
||||
$allStates = [];
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Abstract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\ParticipantFieldHandlerInterface;
|
||||
|
||||
/**
|
||||
@@ -55,12 +55,12 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
|
||||
* if the participant exists at the given index. Returns null if the
|
||||
* participant doesn't exist, preventing array access errors.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing participants (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO containing participants (create or edit)
|
||||
* @param int $participantIndex The index of the participant to retrieve
|
||||
*
|
||||
* @return object|null The participant object, or null if not found
|
||||
*/
|
||||
protected function getParticipant(BookingDtoInterface $bookingDto, int $participantIndex): ?object
|
||||
protected function getParticipant(BookingDto $bookingDto, int $participantIndex): ?object
|
||||
{
|
||||
$participants = $bookingDto->getParticipants();
|
||||
|
||||
@@ -129,12 +129,12 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
|
||||
* processing results.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param BookingDto $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param int $participantIndex The participant index being processed
|
||||
*
|
||||
* @return array<string, array<string, mixed>> Empty array (no state modifications by default)
|
||||
*/
|
||||
public function getFieldStateModifications(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): array
|
||||
public function getFieldStateModifications(array $submittedData, BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,7 @@ use App\Form\Service\Contract\FieldConditionInterface;
|
||||
*/
|
||||
class AdditionalServicesMutabilityCondition implements FieldConditionInterface
|
||||
{
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return false === $bookingDto->travel->additionalServicesMutable;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -54,13 +54,13 @@ class AgeRangeCondition implements FieldConditionInterface
|
||||
* checks if it falls within the configured age range. Returns false if
|
||||
* the participant has no date of birth set.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (unused for age conditions)
|
||||
*
|
||||
* @return bool True if the participant's age meets the criteria, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -21,13 +21,13 @@ class ApplicantCondition implements FieldConditionInterface
|
||||
/**
|
||||
* Evaluates if the participant is the applicant.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (unused)
|
||||
*
|
||||
* @return bool True if the participant is the applicant, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// Default: applicant is the first participant (index 0)
|
||||
return 0 === $participantIndex;
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
use App\Service\ParticipantEligibilityService;
|
||||
|
||||
@@ -36,13 +36,13 @@ class BookingEligibilityCondition implements FieldConditionInterface
|
||||
* Returns true when the participant is INELIGIBLE (should hide service fields).
|
||||
* Returns false when the participant is eligible (show normal form fields).
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data
|
||||
* @param BookingDto $bookingDto The current booking data
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if participant is ineligible (no skipasses available for their age)
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// Invert the eligibility check since conditions typically evaluate to TRUE for "hide"
|
||||
// isParticipantEligible() returns TRUE when eligible, we need TRUE when INELIGIBLE
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -29,13 +29,13 @@ class BulkInsuranceBookingCondition implements FieldConditionInterface
|
||||
* For dependent participants, returns true if the applicant has bulk insurance booking enabled,
|
||||
* regardless of whether an insurance is selected (applies to "no insurance" as well).
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data
|
||||
* @param BookingDto $bookingDto The current booking data
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if bulk insurance booking is active for this dependent participant
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// Applicant is never affected by bulk insurance booking (they control it)
|
||||
if (0 === $participantIndex) {
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -64,13 +64,13 @@ class CompositeCondition implements FieldConditionInterface
|
||||
* evaluation for optimal performance. The evaluation stops as soon as the
|
||||
* final result can be determined.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the composite condition is met, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return match ($this->operator) {
|
||||
self::OPERATOR_AND => $this->evaluateAnd($bookingDto, $participantIndex, $formData),
|
||||
@@ -168,7 +168,7 @@ class CompositeCondition implements FieldConditionInterface
|
||||
* Returns false as soon as any condition evaluates to false,
|
||||
* avoiding unnecessary evaluation of remaining conditions.
|
||||
*/
|
||||
private function evaluateAnd(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
private function evaluateAnd(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
foreach ($this->conditions as $condition) {
|
||||
if (!$condition->evaluate($bookingDto, $participantIndex, $formData)) {
|
||||
@@ -185,7 +185,7 @@ class CompositeCondition implements FieldConditionInterface
|
||||
* Returns true as soon as any condition evaluates to true,
|
||||
* avoiding unnecessary evaluation of remaining conditions.
|
||||
*/
|
||||
private function evaluateOr(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
private function evaluateOr(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
foreach ($this->conditions as $condition) {
|
||||
if ($condition->evaluate($bookingDto, $participantIndex, $formData)) {
|
||||
@@ -199,7 +199,7 @@ class CompositeCondition implements FieldConditionInterface
|
||||
/**
|
||||
* Evaluates NOT logic by inverting the result of the single condition.
|
||||
*/
|
||||
private function evaluateNot(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
private function evaluateNot(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return !$this->conditions[0]->evaluate($bookingDto, $participantIndex, $formData);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -27,17 +27,24 @@ class DateOfBirthProvidedCondition implements FieldConditionInterface
|
||||
* Checks if the participant exists and has a non-null dateOfBirth property.
|
||||
* This is a prerequisite for showing age-dependent form fields and services.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data
|
||||
* @param BookingDto $bookingDto The current booking data
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (unused for this condition)
|
||||
*
|
||||
* @return bool True if the participant has provided their date of birth, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
$result = null !== $participant && null !== $participant->dateOfBirth;
|
||||
|
||||
return null !== $participant && null !== $participant->dateOfBirth;
|
||||
error_log(sprintf('[DOB Condition] Participant %d: dateOfBirth=%s, result=%s',
|
||||
$participantIndex,
|
||||
$participant?->dateOfBirth?->format('Y-m-d') ?? 'NULL',
|
||||
$result ? 'TRUE' : 'FALSE'
|
||||
));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -60,13 +60,13 @@ class FieldValueCondition implements FieldConditionInterface
|
||||
* against the expected value using the configured operator. Supports
|
||||
* both participant-level fields and booking-level fields.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the field value meets the condition criteria, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$fieldValue = $this->getFieldValue($formData, $participantIndex, $bookingDto);
|
||||
|
||||
@@ -175,7 +175,7 @@ class FieldValueCondition implements FieldConditionInterface
|
||||
/**
|
||||
* Retrieves field value from form data or participant data.
|
||||
*/
|
||||
private function getFieldValue(array $formData, int $participantIndex, BookingDtoInterface $bookingDto): mixed
|
||||
private function getFieldValue(array $formData, int $participantIndex, BookingDto $bookingDto): mixed
|
||||
{
|
||||
// First check participant-specific form data
|
||||
if (isset($formData['participants'][$participantIndex][$this->fieldName])) {
|
||||
|
||||
@@ -4,72 +4,36 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
* Condition that determines if insurance fields are editable based on booking and travel dates.
|
||||
* Condition that determines if insurance fields are editable.
|
||||
*
|
||||
* Implements time-based mutability constraints for insurance booking in the edit flow:
|
||||
* - Standard case: Insurance editable up to 30 days before travel date
|
||||
* - Late booking case: If booking made < 30 days before travel, insurance editable up to 3 days after booking date
|
||||
* Insurance cannot be modified via the BusProNet API after booking creation,
|
||||
* so insurance fields are always readonly in edit mode and editable in create mode.
|
||||
*/
|
||||
class InsuranceMutabilityCondition implements FieldConditionInterface
|
||||
{
|
||||
private const DAYS_BEFORE_TRAVEL_THRESHOLD = 30;
|
||||
private const DAYS_AFTER_BOOKING_THRESHOLD = 3;
|
||||
|
||||
/**
|
||||
* Evaluates whether the insurance field should be readonly.
|
||||
*
|
||||
* Returns true if the field should be readonly (locked), false if editable.
|
||||
*
|
||||
* Logic:
|
||||
* 1. Always editable in create flow
|
||||
* 2. In edit flow, check standard case: editable if >= 30 days before travel
|
||||
* 3. In edit flow, check late booking case: editable if within 3 days of booking date
|
||||
* 4. Otherwise: readonly
|
||||
* - Create mode: Always editable (return false)
|
||||
* - Edit mode: Always readonly (return true) - API limitation
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data
|
||||
* @param int $participantIndex The participant index (unused for insurance mutability)
|
||||
* @param array<string, mixed> $formData Current form data (unused for insurance mutability)
|
||||
* @param BookingDto $bookingDto The current booking data
|
||||
* @param int $participantIndex The participant index (unused)
|
||||
* @param array<string, mixed> $formData Current form data (unused)
|
||||
*
|
||||
* @return bool True if field should be readonly, false if editable
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// Only apply mutability constraints to edit flow
|
||||
if (!$bookingDto instanceof BookingEditDto) {
|
||||
return false; // Always editable in create flow
|
||||
}
|
||||
|
||||
$now = \Carbon\Carbon::now()->toDateTimeImmutable();
|
||||
$travelDate = $bookingDto->travel->dateFrom;
|
||||
$bookingDate = $bookingDto->booking->bookingDate;
|
||||
|
||||
// Calculate days until travel
|
||||
$daysUntilTravel = $now->diff($travelDate)->days;
|
||||
$isBeforeTravel = $now < $travelDate;
|
||||
|
||||
// Standard case: Editable if >= 30 days before travel
|
||||
if ($isBeforeTravel && $daysUntilTravel >= self::DAYS_BEFORE_TRAVEL_THRESHOLD) {
|
||||
return false; // Editable
|
||||
}
|
||||
|
||||
// Late booking case: Check if booking was made < 30 days before travel
|
||||
$daysFromBookingToTravel = $bookingDate->diff($travelDate)->days;
|
||||
$wasLateBooking = $daysFromBookingToTravel < self::DAYS_BEFORE_TRAVEL_THRESHOLD;
|
||||
|
||||
if ($wasLateBooking) {
|
||||
// Editable if within 3 days of booking date
|
||||
$daysSinceBooking = $bookingDate->diff($now)->days;
|
||||
|
||||
return $daysSinceBooking > self::DAYS_AFTER_BOOKING_THRESHOLD; // True = readonly (past threshold)
|
||||
}
|
||||
|
||||
// Default: Not editable (readonly)
|
||||
return true;
|
||||
// Insurance cannot be updated via API - always readonly in edit mode
|
||||
return BookingDto::MODE_EDIT === $bookingDto->getMode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,10 +56,6 @@ class InsuranceMutabilityCondition implements FieldConditionInterface
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return sprintf(
|
||||
'Insurance is not editable (>= %d days before travel or > %d days after late booking)',
|
||||
self::DAYS_BEFORE_TRAVEL_THRESHOLD,
|
||||
self::DAYS_AFTER_BOOKING_THRESHOLD
|
||||
);
|
||||
return 'Insurance is not editable in edit mode (API limitation)';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -13,7 +13,7 @@ use App\Form\Service\Contract\FieldConditionInterface;
|
||||
* This is used to set fields as readonly or disabled if the booking or participant
|
||||
* is not allowed to be modified (e.g., after a certain workflow step or status).
|
||||
*
|
||||
* The logic assumes the BookingDtoInterface or its participant DTOs expose a
|
||||
* The logic assumes the BookingDto or its participant DTOs expose a
|
||||
* 'personalDataMutable' property or method. Adjust as needed for your domain.
|
||||
*/
|
||||
class MutabilityCondition implements FieldConditionInterface
|
||||
@@ -21,13 +21,13 @@ class MutabilityCondition implements FieldConditionInterface
|
||||
/**
|
||||
* Evaluates if the participant's personal data is mutable.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (unused)
|
||||
*
|
||||
* @return bool True if the participant's data is mutable, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,7 @@ use App\Form\Service\Contract\FieldConditionInterface;
|
||||
*/
|
||||
class PickupsMutabilityCondition implements FieldConditionInterface
|
||||
{
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return false === $bookingDto->travel->pickupsMutable;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -28,13 +28,13 @@ class RentalSelectionCondition implements FieldConditionInterface
|
||||
* which would indicate rental services have been selected and body
|
||||
* dimensions should be required for proper equipment sizing.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if rental services are selected, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// First check submitted form data for rental selections
|
||||
if (isset($formData['participants'][$participantIndex]['rentals'])) {
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Room;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -35,13 +35,13 @@ class RoomSelectionCondition implements FieldConditionInterface
|
||||
* Checks both submitted form data and participant DTO data to determine
|
||||
* if the selected room matches any of the configured room codes.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if room with matching code is selected, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// First check submitted form data for room selection
|
||||
if (isset($formData['participants'][$participantIndex]['assignedRoomId'])) {
|
||||
@@ -95,11 +95,11 @@ class RoomSelectionCondition implements FieldConditionInterface
|
||||
* Finds a room by ID in the available travel rooms.
|
||||
*
|
||||
* @param int $roomId The room ID to find
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing travel data
|
||||
* @param BookingDto $bookingDto The booking DTO containing travel data
|
||||
*
|
||||
* @return Room|null The found room or null if not found
|
||||
*/
|
||||
private function findRoomById(int $roomId, BookingDtoInterface $bookingDto): ?Room
|
||||
private function findRoomById(int $roomId, BookingDto $bookingDto): ?Room
|
||||
{
|
||||
foreach ($bookingDto->travel->rooms as $room) {
|
||||
if ($room->id === $roomId) {
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -52,13 +52,13 @@ class ServiceSubTypeCondition implements FieldConditionInterface
|
||||
* property against the expected value(s). Handles both form data and
|
||||
* participant DTO data sources.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the service sub-type meets the condition criteria, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$service = $this->getService($formData, $participantIndex, $bookingDto);
|
||||
|
||||
@@ -161,7 +161,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface
|
||||
/**
|
||||
* Retrieves service from form data or participant data.
|
||||
*/
|
||||
private function getService(array $formData, int $participantIndex, BookingDtoInterface $bookingDto): ?Service
|
||||
private function getService(array $formData, int $participantIndex, BookingDto $bookingDto): ?Service
|
||||
{
|
||||
// First check participant-specific form data
|
||||
if (isset($formData['participants'][$participantIndex][$this->serviceFieldName])) {
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -28,13 +28,13 @@ class SkiPassSelectionCondition implements FieldConditionInterface
|
||||
* which would indicate a skipass has been selected and rental services
|
||||
* should be made available with duration filtering applied.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if a skipass is selected, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
// First check submitted form data for skipass selection
|
||||
if (isset($formData['participants'][$participantIndex]['skiPass'])) {
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
@@ -12,7 +12,7 @@ use App\Form\Service\Contract\FieldConditionInterface;
|
||||
*/
|
||||
class TransportationServicesMutabilityCondition implements FieldConditionInterface
|
||||
{
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return false === $bookingDto->travel->transportationServicesMutable;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Contract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Interface for evaluating field state conditions.
|
||||
@@ -29,13 +29,13 @@ interface FieldConditionInterface
|
||||
* state of the booking, participant data, and submitted form values.
|
||||
* The result is used to determine field state (enabled/disabled/readonly).
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (may include partial submissions)
|
||||
*
|
||||
* @return bool True if the condition is met, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool;
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool;
|
||||
|
||||
/**
|
||||
* Returns field names that trigger re-evaluation of this condition.
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Contract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Interface for providing dynamic field options based on context.
|
||||
@@ -42,13 +42,13 @@ interface FieldOptionsProviderInterface
|
||||
* static options defined in the form type.
|
||||
*
|
||||
* @param string $fieldName The name of the field to configure
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being configured
|
||||
* @param array $options Additional options to customize field behavior
|
||||
*
|
||||
* @return array<string, mixed> Symfony form field options, or empty array if field not supported
|
||||
*/
|
||||
public function getFieldOptions(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $options = []): array;
|
||||
public function getFieldOptions(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $options = []): array;
|
||||
|
||||
/**
|
||||
* Checks whether a field has option provider support.
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Contract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Interface for providing dynamic field state based on conditions.
|
||||
@@ -38,13 +38,13 @@ interface FieldStateProviderInterface
|
||||
* condition independently to allow early field exclusion during form building.
|
||||
*
|
||||
* @param string $fieldName The name of the field to evaluate
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the field should be included in the form, false if it should be excluded
|
||||
*/
|
||||
public function shouldIncludeField(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): bool;
|
||||
public function shouldIncludeField(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): bool;
|
||||
|
||||
/**
|
||||
* Calculates the dynamic state for a specified field.
|
||||
@@ -65,13 +65,13 @@ interface FieldStateProviderInterface
|
||||
* - 'attr' => ['class' => 'conditional-field'] - Add CSS classes
|
||||
*
|
||||
* @param string $fieldName The name of the field to evaluate
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data (may include partial submissions)
|
||||
*
|
||||
* @return array<string, mixed> Symfony form field options for state modifications, empty if no changes needed
|
||||
*/
|
||||
public function getFieldState(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array;
|
||||
public function getFieldState(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): array;
|
||||
|
||||
/**
|
||||
* Checks whether a field has state conditions configured.
|
||||
@@ -110,11 +110,11 @@ interface FieldStateProviderInterface
|
||||
* when multiple field states need to be determined simultaneously. It's
|
||||
* particularly useful during form building and bulk state updates.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
|
||||
* @param BookingDto $bookingDto The current booking data for context (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return array<string, array<string, mixed>> Field states indexed by field name
|
||||
*/
|
||||
public function getAllFieldStates(BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array;
|
||||
public function getAllFieldStates(BookingDto $bookingDto, int $participantIndex, array $formData = []): array;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Contract;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Interface for handling dynamic participant form field processing and state modification.
|
||||
@@ -32,10 +32,10 @@ interface ParticipantFieldHandlerInterface
|
||||
* Processes the participant field data from submitted form data and updates the DTO.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The participant index being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void;
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void;
|
||||
|
||||
/**
|
||||
* Determines if this handler should process the field based on submitted participant data.
|
||||
@@ -50,12 +50,12 @@ interface ParticipantFieldHandlerInterface
|
||||
* to enable/disable/hide fields based on the handler's processing results.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param BookingDto $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param int $participantIndex The participant index being processed
|
||||
*
|
||||
* @return array<string, array<string, mixed>> Field state modifications indexed by field name
|
||||
*/
|
||||
public function getFieldStateModifications(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): array;
|
||||
public function getFieldStateModifications(array $submittedData, BookingDto $bookingDto, int $participantIndex): array;
|
||||
|
||||
/**
|
||||
* Returns field names whose state is affected by this handler's processing.
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Service\Abstract\AbstractFieldStateProvider;
|
||||
use App\Form\Service\Condition\ApplicantCondition;
|
||||
use App\Form\Service\Condition\BookingEligibilityCondition;
|
||||
use App\Form\Service\Condition\BulkInsuranceBookingCondition;
|
||||
use App\Form\Service\Condition\CompositeCondition;
|
||||
@@ -34,10 +35,11 @@ use App\Service\ParticipantEligibilityService;
|
||||
class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ParticipantEligibilityService $participantEligibilityService
|
||||
private readonly ParticipantEligibilityService $participantEligibilityService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers field state conditions for the create workflow.
|
||||
*
|
||||
@@ -124,22 +126,7 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
$this->fieldStateConditions['bulkInsuranceBooking'] = [
|
||||
'hidden' => CompositeCondition::or(
|
||||
CompositeCondition::not($dateOfBirthProvidedCondition), // Hide until date of birth provided
|
||||
new class() implements \App\Form\Service\Contract\FieldConditionInterface {
|
||||
public function evaluate(\App\Form\Model\BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
return $participantIndex > 0; // Hide for all participants except applicant
|
||||
}
|
||||
|
||||
public function getDependentFields(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Participant is not the applicant';
|
||||
}
|
||||
}
|
||||
CompositeCondition::not(new ApplicantCondition()) // Hide for non-applicants
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use App\Form\Service\Condition\ApplicantCondition;
|
||||
use App\Form\Service\Condition\CompositeCondition;
|
||||
use App\Form\Service\Condition\DateOfBirthProvidedCondition;
|
||||
use App\Form\Service\Condition\FieldValueCondition;
|
||||
use App\Form\Service\Condition\InsuranceMutabilityCondition;
|
||||
use App\Form\Service\Condition\MutabilityCondition;
|
||||
use App\Form\Service\Condition\PickupsMutabilityCondition;
|
||||
use App\Form\Service\Condition\RentalSelectionCondition;
|
||||
@@ -43,7 +44,8 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
$transportationServicesMutabilityCondition = new TransportationServicesMutabilityCondition();
|
||||
$pickupsMutabilityCondition = new PickupsMutabilityCondition();
|
||||
|
||||
// Make all personal data fields readonly if not mutable OR if applicant
|
||||
// Make all personal data fields readonly if participant not mutable
|
||||
// Note: First participant is now treated as independent from applicant and can be edited
|
||||
$personalDataFields = [
|
||||
'firstName',
|
||||
'lastName',
|
||||
@@ -55,13 +57,15 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
];
|
||||
foreach ($personalDataFields as $field) {
|
||||
$this->fieldStateConditions[$field] = [
|
||||
'readonly' => CompositeCondition::or(
|
||||
new ApplicantCondition(),
|
||||
new MutabilityCondition()
|
||||
),
|
||||
'readonly' => CompositeCondition::not(new MutabilityCondition()),
|
||||
];
|
||||
}
|
||||
|
||||
// Address fields - readonly if participant not mutable
|
||||
$this->fieldStateConditions['address'] = [
|
||||
'readonly' => CompositeCondition::not(new MutabilityCondition()),
|
||||
];
|
||||
|
||||
// Conditional visibility for service fields (same as create flow)
|
||||
$rentalCondition = new RentalSelectionCondition();
|
||||
$skiPassCondition = new SkiPassSelectionCondition();
|
||||
@@ -72,33 +76,40 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
'hidden' => CompositeCondition::not($rentalCondition),
|
||||
];
|
||||
|
||||
// Age-dependent service fields - hidden until birth date provided
|
||||
// Age-dependent service fields - hidden until birth date provided (except for applicant who always has DOB)
|
||||
// Also readonly if services not mutable
|
||||
// For applicant in edit mode: DOB is always available (patched from booking), so never hide
|
||||
$hideUntilDobCondition = CompositeCondition::and(
|
||||
CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
CompositeCondition::not(new ApplicantCondition()) // Don't hide for applicant
|
||||
);
|
||||
|
||||
$this->fieldStateConditions['courses'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['additionalServices'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['board'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
// Skipass - hidden until birth date, readonly if services not mutable
|
||||
// Skipass - hidden until birth date (except applicant), readonly if services not mutable
|
||||
$this->fieldStateConditions['skiPass'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
// Rentals - shown only when skipass selected, readonly if services not mutable
|
||||
// For applicant: only check skipass, not DOB
|
||||
$this->fieldStateConditions['rentals'] = [
|
||||
'hidden' => CompositeCondition::or(
|
||||
CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
$hideUntilDobCondition,
|
||||
CompositeCondition::not($skiPassCondition)
|
||||
),
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
@@ -110,14 +121,14 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
'readonly' => $additionalServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
// Transportation fields - hidden until birth date, readonly if transportation not mutable
|
||||
// Transportation fields - hidden until birth date (except applicant), readonly if transportation not mutable
|
||||
$this->fieldStateConditions['transportationOutbound'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $transportationServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['transportationInbound'] = [
|
||||
'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition),
|
||||
'hidden' => $hideUntilDobCondition,
|
||||
'readonly' => $transportationServicesMutabilityCondition,
|
||||
];
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -84,10 +84,10 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField
|
||||
* 5. Updates participant with filtered valid selections
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
@@ -122,7 +122,7 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField
|
||||
*
|
||||
* @param array $selectedServices List of currently selected services
|
||||
* @param array $availableServices List of all available additional services
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO for context
|
||||
* @param BookingDto $bookingDto The booking DTO for context
|
||||
* @param int $participantIndex The participant index for age evaluation
|
||||
*
|
||||
* @return array Filtered array of valid service selections
|
||||
@@ -130,7 +130,7 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField
|
||||
private function filterValidServiceSelections(
|
||||
array $selectedServices,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): array {
|
||||
$validSelections = [];
|
||||
@@ -156,7 +156,7 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField
|
||||
*
|
||||
* @param mixed $selectedService The selected service to validate
|
||||
* @param array $availableServices Array of available services
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO for context
|
||||
* @param BookingDto $bookingDto The booking DTO for context
|
||||
* @param int $participantIndex The participant index for age evaluation
|
||||
*
|
||||
* @return bool True if the service is valid for the participant, false otherwise
|
||||
@@ -164,7 +164,7 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField
|
||||
private function isServiceValidForParticipant(
|
||||
mixed $selectedService,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): bool {
|
||||
// Find the service in available services
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -53,10 +53,10 @@ class ParticipantAssignedRoomFieldHandler extends AbstractParticipantFieldHandle
|
||||
* 4. Updates the participant's assignedRoomId property
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -49,7 +49,7 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler
|
||||
return true; // Always process to handle deselection cases
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
@@ -72,7 +72,7 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function filterValidServiceSelections(
|
||||
array $selectedServices,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): array {
|
||||
$validSelections = [];
|
||||
@@ -93,7 +93,7 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function isServiceValidForParticipant(
|
||||
mixed $selectedService,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): bool {
|
||||
$service = $this->findServiceInAvailableServices($selectedService, $availableServices);
|
||||
|
||||
@@ -4,30 +4,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
|
||||
/**
|
||||
* Handles bulk insurance booking for the applicant (first participant).
|
||||
* Handles bulk insurance booking checkbox for the applicant (first participant).
|
||||
*
|
||||
* When the applicant enables bulk insurance booking, their selected insurance type
|
||||
* (subType + familyInsurance) is automatically assigned to all participants with
|
||||
* automatic price tier adjustment based on each participant's individual travel price.
|
||||
* This handler only manages the checkbox state - it does NOT assign insurance to
|
||||
* dependent participants. The actual bulk insurance assignment happens in the
|
||||
* BookingDataProcessor during API submission, keeping the form layer clean and
|
||||
* avoiding cross-participant modifications in field handlers.
|
||||
*
|
||||
* This handler processes the bulkInsuranceBooking checkbox state and triggers
|
||||
* insurance assignment to dependent participants when enabled.
|
||||
*
|
||||
* Dependencies: insurance (applicant must have insurance selected before enabling bulk)
|
||||
* The checkbox state is used by:
|
||||
* - Templates: To display "wie Anmelder" for dependent participants
|
||||
* - Processors: To apply applicant's insurance to all participants before API submission
|
||||
*/
|
||||
class ParticipantBulkInsuranceFieldHandler extends AbstractParticipantFieldHandler
|
||||
{
|
||||
public function __construct(
|
||||
private readonly InsuranceMatchingService $insuranceMatchingService,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getFieldName(): string
|
||||
{
|
||||
return 'bulkInsuranceBooking';
|
||||
@@ -35,8 +28,8 @@ class ParticipantBulkInsuranceFieldHandler extends AbstractParticipantFieldHandl
|
||||
|
||||
public function getDependencies(): array
|
||||
{
|
||||
// Depends on insurance field to ensure insurance is selected before bulk assignment
|
||||
return ['insurance'];
|
||||
// No dependencies - just manages checkbox state
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,120 +51,22 @@ class ParticipantBulkInsuranceFieldHandler extends AbstractParticipantFieldHandl
|
||||
/**
|
||||
* Processes the bulk insurance booking checkbox for the applicant.
|
||||
*
|
||||
* When bulk insurance is enabled and applicant has insurance selected,
|
||||
* assigns the same insurance type to all participants based on their
|
||||
* individual pricing and eligibility criteria.
|
||||
* This handler ONLY stores the checkbox state. The actual insurance assignment
|
||||
* to dependent participants is handled by BookingDataProcessor during API submission.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update
|
||||
* @param BookingDto $bookingDto The booking DTO to update
|
||||
* @param int $participantIndex The index of the participant (must be 0)
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant || !$bookingDto instanceof BookingCreateDto) {
|
||||
if (null === $participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get checkbox value from submitted data
|
||||
// Get checkbox value from submitted data and store it
|
||||
$bulkInsuranceBooking = $this->getFieldValue($submittedData, $this->getFieldName());
|
||||
$isBulkEnabled = (bool) $bulkInsuranceBooking;
|
||||
|
||||
// Store checkbox state
|
||||
$participant->bulkInsuranceBooking = $isBulkEnabled;
|
||||
|
||||
// If bulk insurance is enabled and applicant has insurance, assign to all participants
|
||||
if (true === $isBulkEnabled && null !== $participant->insurance) {
|
||||
$this->applyBulkInsuranceToAllParticipants($bookingDto, $participant->insurance);
|
||||
}
|
||||
|
||||
// If bulk insurance was CHANGED from enabled to disabled, clear dependent participants' insurances
|
||||
// Don't clear if it was never enabled (to allow independent insurance selection)
|
||||
if (false === $isBulkEnabled && $this->wasBulkInsurancePreviouslyEnabled($bookingDto)) {
|
||||
$this->clearDependentParticipantsInsurance($bookingDto);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the applicant's insurance type to all participants with automatic price tier adjustment.
|
||||
*
|
||||
* Uses InsuranceMatchingService to find the appropriate price tier for each participant
|
||||
* based on their individual travel price and eligibility criteria.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking DTO with all participants
|
||||
* @param object $applicantInsurance The insurance selected by the applicant
|
||||
*/
|
||||
private function applyBulkInsuranceToAllParticipants(BookingCreateDto $bookingDto, object $applicantInsurance): void
|
||||
{
|
||||
$availableInsurances = $bookingDto->travel->insurances ?? [];
|
||||
|
||||
// Get insurance assignments for all participants
|
||||
$assignments = $this->insuranceMatchingService->batchAssignInsuranceToParticipants(
|
||||
$availableInsurances,
|
||||
$applicantInsurance,
|
||||
$bookingDto
|
||||
);
|
||||
|
||||
// Apply assignments to participants (skip applicant index 0, they keep their selection)
|
||||
foreach ($assignments as $index => $insurance) {
|
||||
if (0 === $index) {
|
||||
continue; // Skip applicant
|
||||
}
|
||||
|
||||
$participant = $bookingDto->getParticipant($index);
|
||||
if (null !== $participant) {
|
||||
$participant->insurance = $insurance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears insurance selections for dependent participants when bulk booking is disabled.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking DTO with all participants
|
||||
*/
|
||||
private function clearDependentParticipantsInsurance(BookingCreateDto $bookingDto): void
|
||||
{
|
||||
foreach ($bookingDto->getParticipants() as $index => $participant) {
|
||||
if (0 === $index) {
|
||||
continue; // Skip applicant
|
||||
}
|
||||
|
||||
$participant->insurance = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bulk insurance was previously enabled by checking if dependent participants
|
||||
* have the same insurance type as the applicant.
|
||||
*
|
||||
* This prevents clearing independent insurance selections when the checkbox is simply unchecked
|
||||
* without ever having been enabled.
|
||||
*
|
||||
* @param BookingCreateDto $bookingDto The booking DTO with all participants
|
||||
*
|
||||
* @return bool True if bulk was previously active (dependent participants have matching insurance)
|
||||
*/
|
||||
private function wasBulkInsurancePreviouslyEnabled(BookingCreateDto $bookingDto): bool
|
||||
{
|
||||
$applicant = $bookingDto->participants[0] ?? null;
|
||||
if (null === $applicant || null === $applicant->insurance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if any dependent participant has insurance that matches the applicant
|
||||
// If so, bulk was likely previously enabled
|
||||
foreach ($bookingDto->participants as $index => $participant) {
|
||||
if (0 === $index) {
|
||||
continue; // Skip applicant
|
||||
}
|
||||
|
||||
if (null !== $participant->insurance) {
|
||||
// If any dependent has insurance, assume bulk was previously enabled
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
$participant->bulkInsuranceBooking = (bool) $bulkInsuranceBooking;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -77,10 +77,10 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler
|
||||
* no longer appropriate for the participant's age are automatically removed.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
@@ -111,7 +111,7 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler
|
||||
*
|
||||
* @param array $selectedServices List of currently selected courses
|
||||
* @param array $availableServices List of all available courses
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO for context
|
||||
* @param BookingDto $bookingDto The booking DTO for context
|
||||
* @param int $participantIndex The participant index for age evaluation
|
||||
*
|
||||
* @return array Filtered array of valid course selections
|
||||
@@ -119,7 +119,7 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function filterValidServiceSelections(
|
||||
array $selectedServices,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): array {
|
||||
$validSelections = [];
|
||||
@@ -142,7 +142,7 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler
|
||||
*
|
||||
* @param mixed $selectedService The selected course to validate
|
||||
* @param array $availableServices Array of available courses
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO for context
|
||||
* @param BookingDto $bookingDto The booking DTO for context
|
||||
* @param int $participantIndex The participant index for age evaluation
|
||||
*
|
||||
* @return bool True if the course is valid for the participant, false otherwise
|
||||
@@ -150,7 +150,7 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function isServiceValidForParticipant(
|
||||
mixed $selectedService,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): bool {
|
||||
// Find the service in available services
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -53,10 +53,10 @@ class ParticipantDateOfBirthFieldHandler extends AbstractParticipantFieldHandler
|
||||
* 4. Updates the participant's dateOfBirth property
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Form\Service;
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\ParticipantDto;
|
||||
use App\Form\Service\Contract\ParticipantFieldHandlerInterface;
|
||||
|
||||
@@ -75,12 +75,14 @@ class ParticipantFieldHandlerRegistry
|
||||
* automatically cleared.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted form data containing participants array
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update with processed field values
|
||||
* @param BookingDto $bookingDto The booking DTO to update with processed field values
|
||||
*
|
||||
* @return array<string, mixed> The synchronized submitted data reflecting DTO changes
|
||||
*/
|
||||
public function processFieldsAndSync(array $submittedData, BookingDtoInterface $bookingDto): array
|
||||
public function processFieldsAndSync(array $submittedData, BookingDto $bookingDto): array
|
||||
{
|
||||
error_log(sprintf('[ProcessFieldsAndSync] Mode: %s', $bookingDto->getMode()));
|
||||
|
||||
// Process all field handlers to clean the DTO
|
||||
$this->processFields($submittedData, $bookingDto);
|
||||
|
||||
@@ -105,9 +107,9 @@ class ParticipantFieldHandlerRegistry
|
||||
* family detection which needs all participants' ages to be processed first).
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted form data containing participants array
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update with processed field values (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update with processed field values (create or edit)
|
||||
*/
|
||||
public function processFields(array $submittedData, BookingDtoInterface $bookingDto): void
|
||||
public function processFields(array $submittedData, BookingDto $bookingDto): void
|
||||
{
|
||||
// Early return if no participant data exists in submission
|
||||
if (false === isset($submittedData['participants']) || false === is_array($submittedData['participants'])) {
|
||||
@@ -245,15 +247,18 @@ class ParticipantFieldHandlerRegistry
|
||||
* that the form continues processing with cleaned data rather than the original
|
||||
* submitted data that may contain invalid selections.
|
||||
*
|
||||
* In edit mode, this also adds the applicant's personal data to the submitted data
|
||||
* for participant 0, ensuring those fields are bound to the DTO by handleRequest.
|
||||
*
|
||||
* The synchronization is generic and works with any field handlers by examining
|
||||
* the current DTO state and updating the corresponding submitted data fields.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The original submitted form data
|
||||
* @param BookingDtoInterface $bookingDto The DTO with cleaned data from field handlers
|
||||
* @param BookingDto $bookingDto The DTO with cleaned data from field handlers
|
||||
*
|
||||
* @return array<string, mixed> Updated submitted data reflecting DTO state
|
||||
*/
|
||||
private function syncSubmittedDataWithDto(array $submittedData, BookingDtoInterface $bookingDto): array
|
||||
private function syncSubmittedDataWithDto(array $submittedData, BookingDto $bookingDto): array
|
||||
{
|
||||
// Ensure participants array exists in submitted data
|
||||
if (false === isset($submittedData['participants']) || false === is_array($submittedData['participants'])) {
|
||||
@@ -272,7 +277,7 @@ class ParticipantFieldHandlerRegistry
|
||||
}
|
||||
|
||||
// Update participant data to match cleaned DTO state
|
||||
$submittedData['participants'][$index] = $this->syncParticipantData($participantData, $participant);
|
||||
$submittedData['participants'][$index] = $this->syncParticipantData($participantData, $participant, $index, $bookingDto);
|
||||
}
|
||||
|
||||
return $submittedData;
|
||||
@@ -285,13 +290,20 @@ class ParticipantFieldHandlerRegistry
|
||||
* any changes made by field handlers. It automatically detects which fields have
|
||||
* been processed by checking against registered handlers.
|
||||
*
|
||||
* In edit mode for participant 0 (applicant), this also adds the personal data fields
|
||||
* that were patched from the applicant, ensuring they're bound to the DTO.
|
||||
*
|
||||
* @param array<string, mixed> $participantData The submitted participant data
|
||||
* @param ParticipantDto $participant The cleaned participant DTO
|
||||
* @param int $index The participant index
|
||||
* @param BookingDto $bookingDto The booking DTO for mode detection
|
||||
*
|
||||
* @return array<string, mixed> Updated participant data with synchronized field values
|
||||
*/
|
||||
private function syncParticipantData(array $participantData, ParticipantDto $participant): array
|
||||
private function syncParticipantData(array $participantData, ParticipantDto $participant, int $index, BookingDto $bookingDto): array
|
||||
{
|
||||
error_log(sprintf('[Sync] Participant %d fields in submission: %s', $participant->index ?? -1, implode(', ', array_keys($participantData))));
|
||||
|
||||
// Sync fields for all registered handlers
|
||||
foreach ($this->handlers as $fieldName => $handler) {
|
||||
// Only sync fields that were in the original submission
|
||||
@@ -299,6 +311,7 @@ class ParticipantFieldHandlerRegistry
|
||||
if (property_exists($participant, $fieldName) && array_key_exists($fieldName, $participantData)) {
|
||||
$dtoValue = $participant->{$fieldName};
|
||||
$participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue);
|
||||
error_log(sprintf('[Sync] Participant %d: synced %s', $participant->index ?? -1, $fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,6 +375,17 @@ class ParticipantFieldHandlerRegistry
|
||||
return $value->id;
|
||||
}
|
||||
|
||||
// Handle Address objects -> convert to array of properties
|
||||
if ($value instanceof \App\BusProNet\Model\Address) {
|
||||
return [
|
||||
'street' => $value->street,
|
||||
'postCode' => $value->postCode,
|
||||
'city' => $value->city,
|
||||
'district' => $value->district,
|
||||
'country' => $value->country,
|
||||
];
|
||||
}
|
||||
|
||||
// Handle DateTimeInterface -> convert to string format
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return $value->format('Y-m-d');
|
||||
|
||||
@@ -9,7 +9,7 @@ use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\BookingEditDto;
|
||||
use App\Form\Service\Abstract\AbstractFieldOptionsProvider;
|
||||
use App\Form\Service\Factory\ParticipantRoomChoiceLoaderFactory;
|
||||
@@ -82,11 +82,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
protected function registerFieldOptionProviders(): void
|
||||
{
|
||||
// Room assignment field provider (available for both create and edit workflows)
|
||||
$this->fieldOptionProviders['assignedRoomId'] = function (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) {
|
||||
$this->fieldOptionProviders['assignedRoomId'] = function (BookingDto $bookingDto, int $participantIndex, array $options = []) {
|
||||
$choiceLoader = null;
|
||||
$disabled = false;
|
||||
|
||||
if ($bookingDto instanceof BookingCreateDto) {
|
||||
if (BookingDto::MODE_CREATE === $bookingDto->getMode()) {
|
||||
// Create context: use selected rooms from step 1
|
||||
$choiceLoader = $this->roomChoiceLoaderFactory->createForCreate(
|
||||
$bookingDto->participants,
|
||||
@@ -95,7 +95,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
);
|
||||
// Disable when only one room type selected (auto-assigned)
|
||||
$disabled = 1 === count($bookingDto->getSelectedRooms());
|
||||
} elseif ($bookingDto instanceof BookingEditDto) {
|
||||
} elseif (BookingDto::MODE_EDIT === $bookingDto->getMode()) {
|
||||
// Edit context: use already-booked rooms from booking
|
||||
$choiceLoader = $this->roomChoiceLoaderFactory->createForEdit(
|
||||
$bookingDto->participants,
|
||||
@@ -121,7 +121,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
};
|
||||
|
||||
// Courses field provider - provides age-appropriate courses from travel data
|
||||
$this->fieldOptionProviders['courses'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['courses'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Kurse',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
@@ -156,7 +156,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Additional services field provider - provides age-appropriate additional services with mandatory pre-selection
|
||||
$this->fieldOptionProviders['additionalServices'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['additionalServices'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Zusatzleistungen',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
@@ -199,7 +199,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Board field provider - provides age-appropriate board options from travel data
|
||||
$this->fieldOptionProviders['board'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['board'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Verpflegung',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
@@ -229,7 +229,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Rentals field provider - provides age-appropriate rental options filtered by selected skipass duration
|
||||
$this->fieldOptionProviders['rentals'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['rentals'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Leihmaterial',
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
@@ -268,7 +268,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Rental insurance field provider - provides rental insurance options when rental services are selected
|
||||
$this->fieldOptionProviders['rentalInsurance'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['rentalInsurance'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => $this->getRentalInsuranceCheckboxLabel($bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true, true)),
|
||||
'required' => false,
|
||||
'property_path' => 'rentalInsuranceSelected',
|
||||
@@ -276,7 +276,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// License plate field provider - provides text input for vehicle license plate when parking is selected
|
||||
$this->fieldOptionProviders['licensePlate'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['licensePlate'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Kennzeichen',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
@@ -288,7 +288,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Skipass field provider - provides age-appropriate skipass options from travel data filtered by date range
|
||||
$this->fieldOptionProviders['skiPass'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['skiPass'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Skipass',
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
@@ -323,7 +323,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Room remarks field provider - provides textarea for room-specific remarks (only for 'mbz' rooms)
|
||||
$this->fieldOptionProviders['remarksRoom'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['remarksRoom'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Wünsche oder Anmerkungen zum Zimmer',
|
||||
'required' => false,
|
||||
'sanitize_html' => true,
|
||||
@@ -335,7 +335,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
// Transportation field providers - handles outbound/inbound transportation and pickup selection
|
||||
|
||||
// Outbound Transportation
|
||||
$this->fieldOptionProviders['transportationOutbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['transportationOutbound'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Hinfahrt',
|
||||
'choices' => $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::OUTBOUND_TRAVEL),
|
||||
'choice_label' => fn (Service $service) => $this->formatTransportationServiceLabel($service),
|
||||
@@ -366,7 +366,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Inbound Transportation
|
||||
$this->fieldOptionProviders['transportationInbound'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['transportationInbound'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Rückfahrt',
|
||||
'choices' => $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::INBOUND_TRAVEL),
|
||||
'choice_label' => fn (Service $service) => $this->formatTransportationServiceLabel($service),
|
||||
@@ -398,7 +398,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
// Pickup (conditional - only shown when either transportation direction is bus)
|
||||
// Uses outbound pickups list, applies to both directions
|
||||
$this->fieldOptionProviders['pickup'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['pickup'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Zu- und Ausstieg',
|
||||
'choices' => $bookingDto->travel->pickupsOutbound,
|
||||
'choice_label' => fn (?Pickup $pickup) => $this->formatPickupLabelWithPrice($pickup),
|
||||
@@ -411,13 +411,14 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
|
||||
// Parking (conditional - only shown when outbound transportation is PKW)
|
||||
// Simple checkbox since there's only ever one parking type
|
||||
$this->fieldOptionProviders['parking'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
$this->fieldOptionProviders['parking'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => $this->getParkingCheckboxLabel($bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_PARKING, true)),
|
||||
'required' => false,
|
||||
];
|
||||
|
||||
// Bulk insurance booking checkbox (applicant only - controls insurance assignment for all participants)
|
||||
$this->fieldOptionProviders['bulkInsuranceBooking'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
// Only registered in create mode - insurance cannot be modified in edit mode due to API limitation
|
||||
$this->fieldOptionProviders['bulkInsuranceBooking'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Für alle Teilnehmer buchen',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
@@ -428,7 +429,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
];
|
||||
|
||||
// Insurance field provider - provides age and eligibility filtered insurances for participants
|
||||
$this->fieldOptionProviders['insurance'] = fn (BookingDtoInterface $bookingDto, int $participantIndex, array $options = []) => [
|
||||
// Only registered in create mode - insurance cannot be modified in edit mode due to API limitation
|
||||
$this->fieldOptionProviders['insurance'] = fn (BookingDto $bookingDto, int $participantIndex, array $options = []) => [
|
||||
'label' => 'Reiseversicherung',
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
@@ -461,12 +463,12 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* is only available for the exact duration of the skipass.
|
||||
*
|
||||
* @param array $rentals Array of rental Service objects to filter
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing participant data
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
*
|
||||
* @return array Filtered array of rentals matching skipass duration
|
||||
*/
|
||||
private function filterRentalsBySkiPassDuration(array $rentals, BookingDtoInterface $bookingDto, int $participantIndex): array
|
||||
private function filterRentalsBySkiPassDuration(array $rentals, BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant || null === $participant->skiPass) {
|
||||
@@ -586,12 +588,12 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* Checks if a service should be rendered as read-only due to unavailability.
|
||||
*
|
||||
* @param Service $service The service to check
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing participant data
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant currently selecting services
|
||||
*
|
||||
* @return bool True if the service should be read-only due to unavailability
|
||||
*/
|
||||
private function isServiceUnavailableForParticipant(Service $service, BookingDtoInterface $bookingDto, int $participantIndex): bool
|
||||
private function isServiceUnavailableForParticipant(Service $service, BookingDto $bookingDto, int $participantIndex): bool
|
||||
{
|
||||
if (!$bookingDto instanceof BookingCreateDto) {
|
||||
// For non-create workflows, don't apply availability restrictions
|
||||
@@ -609,12 +611,12 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
* returns empty array to be handled by field visibility conditions.
|
||||
*
|
||||
* @param array $services Array of Service objects to filter
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing participant data
|
||||
* @param BookingDto $bookingDto The booking DTO containing participant data
|
||||
* @param int $participantIndex Index of the participant to evaluate
|
||||
*
|
||||
* @return array Filtered array of available services
|
||||
*/
|
||||
private function filterServicesByAgeConstraints(array $services, BookingDtoInterface $bookingDto, int $participantIndex): array
|
||||
private function filterServicesByAgeConstraints(array $services, BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
$ageEvaluator = new ServiceAgeEvaluator();
|
||||
|
||||
@@ -664,12 +666,12 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
/**
|
||||
* Gets eligible insurances for a participant based on eligibility criteria.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing travel and participant data
|
||||
* @param BookingDto $bookingDto The booking DTO containing travel and participant data
|
||||
* @param int $participantIndex The index of the participant to get eligible insurances for
|
||||
*
|
||||
* @return array Array of eligible insurance objects filtered by age, family status, and other constraints
|
||||
*/
|
||||
private function getEligibleInsurances(BookingDtoInterface $bookingDto, int $participantIndex): array
|
||||
private function getEligibleInsurances(BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant) {
|
||||
@@ -682,11 +684,6 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
|
||||
// They are only available as part of packages
|
||||
$availableInsurances = array_filter($availableInsurances, fn ($insurance) => !$insurance->complementary);
|
||||
|
||||
// Only apply insurance filtering for BookingCreateDto (creation workflow)
|
||||
if (!$bookingDto instanceof BookingCreateDto) {
|
||||
return $availableInsurances;
|
||||
}
|
||||
|
||||
// Use insurance matching service to filter based on eligibility criteria
|
||||
return $this->insuranceMatchingService->getEligibleInsurances(
|
||||
$availableInsurances,
|
||||
|
||||
@@ -5,8 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Insurance;
|
||||
use App\Form\Model\BookingCreateDto;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
use App\Service\InsuranceMatchingService;
|
||||
|
||||
@@ -112,13 +111,13 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
|
||||
* participant data and automatically reassigns to the correct price tier if needed.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null === $participant || !$bookingDto instanceof BookingCreateDto) {
|
||||
if (null === $participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,12 +205,12 @@ class ParticipantInsuranceFieldHandler extends AbstractParticipantFieldHandler
|
||||
* Currently no field state modifications are needed for insurance selection.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param BookingDto $bookingDto The booking DTO (potentially modified by processing)
|
||||
* @param int $participantIndex The participant index being processed
|
||||
*
|
||||
* @return array<string, array<string, mixed>> Empty array - no field state modifications
|
||||
*/
|
||||
public function getFieldStateModifications(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): array
|
||||
public function getFieldStateModifications(array $submittedData, BookingDto $bookingDto, int $participantIndex): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -69,10 +69,10 @@ class ParticipantLicensePlateFieldHandler extends AbstractParticipantFieldHandle
|
||||
* the license plate is automatically cleared to maintain data consistency.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Form\Service;
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ class ParticipantParkingFieldHandler extends AbstractParticipantFieldHandler
|
||||
return true; // Always process for state changes
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
@@ -95,11 +95,11 @@ class ParticipantParkingFieldHandler extends AbstractParticipantFieldHandler
|
||||
* Gets the first (and typically only) parking service for pricing calculation.
|
||||
* Returns null if no parking services are available.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing travel data
|
||||
* @param BookingDto $bookingDto The booking DTO containing travel data
|
||||
*
|
||||
* @return Service|null The parking service object, or null if not found
|
||||
*/
|
||||
private function findParkingService(BookingDtoInterface $bookingDto): ?Service
|
||||
private function findParkingService(BookingDto $bookingDto): ?Service
|
||||
{
|
||||
$parkingServices = $bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_PARKING, true);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ class ParticipantPickupFieldHandler extends AbstractParticipantFieldHandler
|
||||
return true; // Always process to handle clearing pickup
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -41,10 +41,10 @@ class ParticipantRemarksRoomFieldHandler extends AbstractParticipantFieldHandler
|
||||
* the normalization of empty strings to null values.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -74,10 +74,10 @@ class ParticipantRentalInsuranceFieldHandler extends AbstractParticipantFieldHan
|
||||
* is no longer appropriate for the participant's age, it is automatically cleared.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
@@ -118,11 +118,11 @@ class ParticipantRentalInsuranceFieldHandler extends AbstractParticipantFieldHan
|
||||
* Gets the first (and typically only) rental insurance service.
|
||||
* Returns null if no rental insurance services are available.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO containing travel data
|
||||
* @param BookingDto $bookingDto The booking DTO containing travel data
|
||||
*
|
||||
* @return Service|null The rental insurance service object, or null if not found
|
||||
*/
|
||||
private function findRentalInsuranceService(BookingDtoInterface $bookingDto): ?Service
|
||||
private function findRentalInsuranceService(BookingDto $bookingDto): ?Service
|
||||
{
|
||||
$rentalInsuranceServices = $bookingDto->travel->getAdditionalServicesBySubTypes(Constants::TOKEN_RENTAL_INSURANCE, true, true);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
|
||||
return true; // Always process to handle deselection cases
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
@@ -108,7 +108,7 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function filterValidServiceSelections(
|
||||
array $selectedServices,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): array {
|
||||
$validSelections = [];
|
||||
@@ -129,7 +129,7 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function isServiceValidForParticipant(
|
||||
mixed $selectedService,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): bool {
|
||||
$service = $this->findServiceInAvailableServices($selectedService, $availableServices);
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Constants;
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -79,10 +79,10 @@ class ParticipantSkiPassFieldHandler extends AbstractParticipantFieldHandler
|
||||
* age or exceeds the travel date range, it is automatically cleared.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO to update (create or edit)
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
@@ -117,7 +117,7 @@ class ParticipantSkiPassFieldHandler extends AbstractParticipantFieldHandler
|
||||
*
|
||||
* @param mixed $selectedService The selected skipass to validate
|
||||
* @param array $availableServices Array of available skipasses
|
||||
* @param BookingDtoInterface $bookingDto The booking DTO for context
|
||||
* @param BookingDto $bookingDto The booking DTO for context
|
||||
* @param int $participantIndex The participant index for age evaluation
|
||||
*
|
||||
* @return bool True if the skipass is valid for the participant, false otherwise
|
||||
@@ -125,7 +125,7 @@ class ParticipantSkiPassFieldHandler extends AbstractParticipantFieldHandler
|
||||
private function isServiceValidForParticipant(
|
||||
mixed $selectedService,
|
||||
array $availableServices,
|
||||
BookingDtoInterface $bookingDto,
|
||||
BookingDto $bookingDto,
|
||||
int $participantIndex,
|
||||
): bool {
|
||||
// Find the service in available services
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ class ParticipantTransportationInboundFieldHandler extends AbstractParticipantFi
|
||||
return true; // Always process to handle deselection cases
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\BusProNet\Utility\DirectionMapper;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ class ParticipantTransportationOutboundFieldHandler extends AbstractParticipantF
|
||||
return true; // Always process to handle deselection cases
|
||||
}
|
||||
|
||||
public function processField(array $submittedData, BookingDtoInterface $bookingDto, int $participantIndex): void
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
|
||||
/**
|
||||
* Evaluates service availability based on participant age constraints.
|
||||
@@ -40,12 +40,12 @@ class ServiceAgeEvaluator
|
||||
* age at the time of travel, not their current age.
|
||||
*
|
||||
* @param Service $service The service to evaluate
|
||||
* @param BookingDtoInterface $bookingDto The booking containing participant data
|
||||
* @param BookingDto $bookingDto The booking containing participant data
|
||||
* @param int $participantIndex The index of the participant to evaluate
|
||||
*
|
||||
* @return bool True if the service is available for the participant
|
||||
*/
|
||||
public function isServiceAvailableForParticipant(Service $service, BookingDtoInterface $bookingDto, int $participantIndex): bool
|
||||
public function isServiceAvailableForParticipant(Service $service, BookingDto $bookingDto, int $participantIndex): bool
|
||||
{
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Trait;
|
||||
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Model\BookingDto;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
@@ -17,17 +17,17 @@ use Symfony\Component\Form\FormInterface;
|
||||
trait FormTraversalTrait
|
||||
{
|
||||
/**
|
||||
* Gets the BookingDtoInterface from the root of the form tree.
|
||||
* Gets the BookingDto from the root of the form tree.
|
||||
*
|
||||
* This helper method traverses up the form tree to find the root form
|
||||
* and extracts the BookingDtoInterface data. This is shared logic used
|
||||
* and extracts the BookingDto data. This is shared logic used
|
||||
* by both create and edit participant forms.
|
||||
*
|
||||
* @param FormInterface $form The form to start traversing from
|
||||
*
|
||||
* @return BookingDtoInterface|null The booking DTO or null if not found
|
||||
* @return BookingDto|null The booking DTO or null if not found
|
||||
*/
|
||||
public function getBookingDtoFromForm(FormInterface $form): ?BookingDtoInterface
|
||||
public function getBookingDtoFromForm(FormInterface $form): ?BookingDto
|
||||
{
|
||||
// Traverse up the form tree to get the root form's data
|
||||
$rootForm = $form;
|
||||
@@ -37,6 +37,6 @@ trait FormTraversalTrait
|
||||
|
||||
$data = $rootForm->getData();
|
||||
|
||||
return $data instanceof BookingDtoInterface ? $data : null;
|
||||
return $data instanceof BookingDto ? $data : null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user