$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 } public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void { $participant = $this->getParticipant($bookingDto, $participantIndex); if (null === $participant) { return; } if (false === $participant instanceof ParticipantDto) { return; } $selectedBoard = $this->getFieldValue($submittedData, $this->getFieldName()) ?? []; // Get available board options from travel data (includes booked options in edit mode) $availableBoard = $this->getAvailableServicesWithBooked( $bookingDto, $participantIndex, Constants::TOKEN_BOARD ); $validSelections = $this->filterValidServiceSelections( $selectedBoard, $availableBoard, $bookingDto, $participantIndex ); $this->updateAutoBookOptOutBoard( $participant, $availableBoard, $validSelections, $bookingDto, $participantIndex ); $participant->board = $validSelections; } private function updateAutoBookOptOutBoard( ParticipantDto $participant, array $availableBoard, array $validSelections, BookingDto $bookingDto, int $participantIndex, ): void { $currentlySelectedAutoBookIds = []; foreach ($participant->board as $selectedService) { if (false === $selectedService instanceof Service) { continue; } if (null === $selectedService->id || true === $selectedService->mandatory || false === $selectedService->autoBook) { continue; } if (false === $this->isServiceValidForParticipant($selectedService, $availableBoard, $bookingDto, $participantIndex)) { continue; } $currentlySelectedAutoBookIds[] = $selectedService->id; } $newlySelectedAutoBookIds = []; foreach ($validSelections as $selectedService) { if (null === $selectedService->id || true === $selectedService->mandatory || false === $selectedService->autoBook) { continue; } $newlySelectedAutoBookIds[] = $selectedService->id; } foreach ($currentlySelectedAutoBookIds as $serviceId) { if (false === in_array($serviceId, $newlySelectedAutoBookIds, true) && false === in_array($serviceId, $participant->autoBookOptOutBoardIds, true)) { $participant->autoBookOptOutBoardIds[] = $serviceId; } } $participant->autoBookOptOutBoardIds = array_values(array_filter( $participant->autoBookOptOutBoardIds, static fn (int $serviceId): bool => false === in_array($serviceId, $newlySelectedAutoBookIds, true) )); } private function filterValidServiceSelections( array $selectedServices, array $availableServices, BookingDto $bookingDto, int $participantIndex, ): array { $validSelections = []; foreach ($selectedServices as $selectedService) { if ($this->isServiceValidForParticipant($selectedService, $availableServices, $bookingDto, $participantIndex)) { // Convert the selected service ID/data back to the actual Service object $serviceObject = $this->findServiceInAvailableServices($selectedService, $availableServices); if (null !== $serviceObject) { $validSelections[] = $serviceObject; } } } return $validSelections; } private function isServiceValidForParticipant( mixed $selectedService, array $availableServices, BookingDto $bookingDto, int $participantIndex, ): bool { $service = $this->findServiceInAvailableServices($selectedService, $availableServices); if (null === $service) { return false; } $ageEvaluator = new ServiceAgeEvaluator(); if (false === $ageEvaluator->canEvaluate($service)) { return true; } return $ageEvaluator->isServiceAvailableForParticipant($service, $bookingDto, $participantIndex); } }