*/ private array $requiredRoomCodes; private string $matchMode; /** * Creates a new room selection condition for specified room codes. * * @param array $requiredRoomCodes Room codes that should trigger this condition * @param string $matchMode Match mode: 'prefix' (default) or 'exact' */ public function __construct(array $requiredRoomCodes, string $matchMode = self::MATCH_MODE_PREFIX) { $this->requiredRoomCodes = array_map('strtolower', $requiredRoomCodes); $this->matchMode = $matchMode; } /** * Evaluates whether the participant has selected a room with one of the required codes. * * Checks both submitted form data and participant DTO data to determine * if the selected room matches any of the configured room codes. * * @param BookingDto $bookingDto The current booking data (create or edit) * @param int $participantIndex The index of the participant being evaluated * @param array $formData Current form data for condition evaluation * * @return bool True if room with matching code is selected, false otherwise */ public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool { // First check submitted form data for room selection if (isset($formData['participants'][$participantIndex]['assignedRoomId'])) { $selectedRoomId = $formData['participants'][$participantIndex]['assignedRoomId']; if (true === is_numeric($selectedRoomId)) { $room = $this->findRoomById((int) $selectedRoomId, $bookingDto); if (null !== $room && $this->matchesRoomCode($room->code ?? '')) { return true; } } } // Then check participant DTO data for existing room selection $participant = $bookingDto->getParticipant($participantIndex); if (null !== $participant && null !== $participant->assignedRoomId) { $room = $this->findRoomById($participant->assignedRoomId, $bookingDto); if (null !== $room && $this->matchesRoomCode($room->code ?? '')) { return true; } } return false; } /** * Returns field names that trigger re-evaluation of this condition. * * This condition depends on the assignedRoomId field, so any changes to room * selection should trigger re-evaluation of dependent fields. * * @return string[] Array containing the field names that affect this condition */ public function getDependentFields(): array { return ['assignedRoomId']; } /** * Returns a human-readable description of this condition. * * @return string A brief description of the condition logic */ public function getDescription(): string { $codes = implode(', ', $this->requiredRoomCodes); $matchType = self::MATCH_MODE_PREFIX === $this->matchMode ? 'starting with' : 'matching'; return "Field visible when room with code(s) {$matchType} [{$codes}] is selected"; } /** * Finds a room by ID in the available travel rooms. * * @param int $roomId The room ID to find * @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, BookingDto $bookingDto): ?Room { foreach ($bookingDto->travel->rooms as $room) { if ($room->id === $roomId) { return $room; } } return null; } /** * Checks if a room code matches any of the required codes based on match mode. * * @param string $roomCode The room code to check * * @return bool True if the room code matches, false otherwise */ private function matchesRoomCode(string $roomCode): bool { $roomCode = strtolower($roomCode); if (self::MATCH_MODE_PREFIX === $this->matchMode) { foreach ($this->requiredRoomCodes as $code) { if (str_starts_with($roomCode, $code)) { return true; } } return false; } return in_array($roomCode, $this->requiredRoomCodes, true); } }