$submittedData The submitted participant form data * @param string $mode The booking mode (BookingDto::MODE_CREATE or MODE_EDIT) * @param int $participantIndex The index of the participant being processed * * @return bool Always returns true for service selection fields */ public function shouldProcess(array $submittedData, string $mode, int $participantIndex): bool { return true; // Always process to handle deselection cases } /** * Processes the skiPass field for a specific participant. * * This method extracts the skipass selection from submitted form data, * validates the selection against the participant's age constraints and * travel date constraints, and updates the participant DTO with the valid * selection. If the skipass is no longer appropriate for the participant's * age or exceeds the travel date range, it is automatically cleared. * * @param array $submittedData The submitted participant form data * @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, BookingDto $bookingDto, int $participantIndex): void { // Safely get the participant object, returning early if not found $participant = $this->getParticipant($bookingDto, $participantIndex); if (null === $participant) { return; } if (false === $participant instanceof ParticipantDto) { return; } // Extract current skipass selection from submitted data $selectedSkiPass = $this->getFieldValue($submittedData, $this->getFieldName()); // Get all skipasses from travel data (includes booked skipasses in edit mode) $availableSkipasses = $this->getAvailableServicesWithBooked( $bookingDto, $participantIndex, Constants::TOKEN_SKI_PASS, true ); $currentlySelectedAutoBookSkiPassId = $this->getSelectedAutoBookSkiPassId( $participant, $availableSkipasses, $bookingDto, $participantIndex ); // For single selection, validate the selected skipass and convert ID to Service object $validSelection = null; if (null !== $selectedSkiPass) { if ($this->isServiceValidForParticipant($selectedSkiPass, $availableSkipasses, $bookingDto, $participantIndex)) { // Convert the submitted ID back to the Service object $validSelection = $this->findServiceInAvailableServices($selectedSkiPass, $availableSkipasses); } } $this->updateAutoBookOptOutSkiPasses($participant, $currentlySelectedAutoBookSkiPassId, $validSelection); // Update participant with validated selection $participant->skiPass = $validSelection; } /** @param array $availableSkipasses */ private function getSelectedAutoBookSkiPassId( ParticipantDto $participant, array $availableSkipasses, BookingDto $bookingDto, int $participantIndex, ): ?int { $currentSkiPass = $participant->skiPass; if (null === $currentSkiPass || null === $currentSkiPass->id) { return null; } if (true === $currentSkiPass->mandatory || false === $currentSkiPass->autoBook) { return null; } if (false === $this->isServiceValidForParticipant($currentSkiPass, $availableSkipasses, $bookingDto, $participantIndex)) { return null; } return $currentSkiPass->id; } private function updateAutoBookOptOutSkiPasses( ParticipantDto $participant, ?int $currentlySelectedAutoBookSkiPassId, ?Service $validSelection, ): void { $newlySelectedAutoBookSkiPassId = null; if (null !== $validSelection && null !== $validSelection->id && false === $validSelection->mandatory && true === $validSelection->autoBook) { $newlySelectedAutoBookSkiPassId = $validSelection->id; } if (null !== $currentlySelectedAutoBookSkiPassId && $currentlySelectedAutoBookSkiPassId !== $newlySelectedAutoBookSkiPassId && false === in_array($currentlySelectedAutoBookSkiPassId, $participant->autoBookOptOutSkiPassIds, true)) { $participant->autoBookOptOutSkiPassIds[] = $currentlySelectedAutoBookSkiPassId; } if (null !== $newlySelectedAutoBookSkiPassId) { $participant->autoBookOptOutSkiPassIds = array_values(array_filter( $participant->autoBookOptOutSkiPassIds, static fn (int $serviceId): bool => $serviceId !== $newlySelectedAutoBookSkiPassId )); } } /** * Validates if a selected skipass is still valid for the participant. * * This method checks both age constraints (via birth year ranges) and * date constraints (skipass dates must be within travel dates). * * @param mixed $selectedService The selected skipass to validate * @param array $availableServices Array of available skipasses * @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 */ private function isServiceValidForParticipant( mixed $selectedService, array $availableServices, BookingDto $bookingDto, int $participantIndex, ): bool { // Find the service in available services $service = $this->findServiceInAvailableServices($selectedService, $availableServices); if (null === $service) { return false; // Service not found in available services } // Check if service has age constraints $ageEvaluator = new ServiceAgeEvaluator(); if ($ageEvaluator->canEvaluate($service)) { // Validate service against participant's age if (false === $ageEvaluator->isServiceAvailableForParticipant($service, $bookingDto, $participantIndex)) { return false; // Age constraints not met } } // Date constraints are already handled by the travel->getAdditionalServicesBySubTypes // method with date filtering enabled (true, true parameters), so if the service // is in the available services list, it already passed date validation. return true; // Service passed both age and date validation } }