fieldStateConditions['fieldName'] = [ * 'readonly' => new SomeCondition(), * 'disabled' => CompositeCondition::and( * new AgeRangeCondition(null, 17), * FieldValueCondition::equals('someField', 'someValue') * ), * ]; * * Condition Types: * - 'readonly': Field is visible but not editable * - 'disabled': Field interaction is disabled * - 'required': Field becomes mandatory * - 'hidden': Field is not displayed */ protected function registerFieldStateConditions(): void { // Booking eligibility condition - hide all service fields when no skipasses available for participant's age $bookingEligibilityCondition = new BookingEligibilityCondition($this->participantEligibilityService); $rentalCondition = new RentalSelectionCondition(); $skiPassCondition = new SkiPassSelectionCondition(); // First participant read-only condition // Render personal data fields as static text for first participant in non-internal agency bookings // when user is logged in (data is prepopulated from BPN account) $firstParticipantReadOnlyCondition = new FirstParticipantReadOnlyCondition(); $this->fieldStateConditions['firstName'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['lastName'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['gender'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['nationality'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['dateOfBirth'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['email'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; // Mobile field: static text for first participant (when read-only), required for first participant (guest bookings) $this->fieldStateConditions['mobile'] = [ 'static_text' => $firstParticipantReadOnlyCondition, 'required' => new FirstParticipantCondition(), ]; // Address subfields - must render as static text to prevent creating duplicate BPN records $this->fieldStateConditions['address.street'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.postCode'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.city'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.country'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; $this->fieldStateConditions['address.district'] = [ 'static_text' => $firstParticipantReadOnlyCondition, ]; // Note: Body dimensions (height, weight, shoeSize) are NOT hidden for authenticated users // These are preferences/measurements that can be updated without creating duplicate records // Hide body dimensions section unless rental services are selected $this->fieldStateConditions['bodyDimensions'] = [ 'hidden' => CompositeCondition::not($rentalCondition), ]; // Hide age-dependent fields when no date of birth is provided $dateOfBirthProvidedCondition = new DateOfBirthProvidedCondition(); // Baby age condition - hide ski pass for babies (0-2 years) $babyAgeCondition = new BabyAgeCondition(); // Age-dependent service fields are hidden until birth date is provided OR when participant is ineligible $this->fieldStateConditions['courses'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition ), ]; $this->fieldStateConditions['additionalServices'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition ), ]; // Skipass field - hidden when: // - No date of birth provided // - Participant is ineligible (no skipasses for their age) // - Participant is baby age (0-2 years) - babies don't need ski passes $this->fieldStateConditions['skiPass'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition, $babyAgeCondition ), ]; // Show rentals only when both date of birth is provided AND skipass is selected AND participant is eligible $this->fieldStateConditions['rentals'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), CompositeCondition::not($skiPassCondition), $bookingEligibilityCondition ), ]; $this->fieldStateConditions['board'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition ), ]; // Bulk insurance booking conditions $bulkInsuranceBookingCondition = new BulkInsuranceBookingCondition(); // Show bulk insurance booking checkbox ONLY for first participant (index 0) when: // - Date of birth is provided // - There are multiple participants (bulk assignment is meaningless for single participant) $this->fieldStateConditions['bulkInsuranceBooking'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), // Hide until date of birth provided CompositeCondition::not(new FirstParticipantCondition()), // Hide for non-first participants CompositeCondition::not(new MultipleParticipantsCondition()) // Hide when only one participant ), ]; // Hide insurance field until date of birth is provided OR when bulk insurance booking is active OR when participant is ineligible $this->fieldStateConditions['insurance'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bulkInsuranceBookingCondition, $bookingEligibilityCondition ), ]; // Room-specific field conditions $sharedRoomCondition = new RoomSelectionCondition(['mbz']); // Show remarks room field only when room with code 'mbz' is selected $this->fieldStateConditions['remarksRoom'] = [ 'hidden' => CompositeCondition::not($sharedRoomCondition), ]; // Transportation-related field conditions // All transportation fields require date of birth and participant eligibility // Outbound transportation - hidden until date of birth provided AND participant is eligible $this->fieldStateConditions['transportationOutbound'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition ), ]; // Inbound transportation - hidden until date of birth provided AND participant is eligible $this->fieldStateConditions['transportationInbound'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($dateOfBirthProvidedCondition), $bookingEligibilityCondition ), ]; // Show pickup only when either outbound or inbound transportation is BUS (hidden by default) $this->fieldStateConditions['pickup'] = [ 'hidden' => CompositeCondition::not( CompositeCondition::or( ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_BUS_API), ServiceSubTypeCondition::equals('transportationInbound', DirectionMapper::SUBTYPE_BUS_API) ) ), ]; // Show parking only when outbound transportation is PKW AND participant is 18+ (hidden by default) // Parking is offered at holiday destination for those arriving by car // Participants under 18 cannot drive in Germany, so parking is not relevant for them $minimumDrivingAgeCondition = new AgeRangeCondition(18); $this->fieldStateConditions['parking'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not( ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API) ), CompositeCondition::not($minimumDrivingAgeCondition) ), ]; // Show rental insurance only when rental services are selected AND LVS services exist (hidden by default) $rentalInsuranceAvailableCondition = new RentalInsuranceAvailableCondition(); $this->fieldStateConditions['rentalInsurance'] = [ 'hidden' => CompositeCondition::or( CompositeCondition::not($rentalCondition), CompositeCondition::not($rentalInsuranceAvailableCondition) ), ]; // Show license plate only when parking is selected (hidden by default) $this->fieldStateConditions['licensePlate'] = [ 'hidden' => FieldValueCondition::equals('parking', false), ]; // Make address field required for first participant (index 0) // Also render as static text for first participant when read-only $this->fieldStateConditions['address'] = [ 'static_text' => $firstParticipantReadOnlyCondition, 'required' => new FirstParticipantCondition(), ]; // Room assignment field: // - Hidden until date of birth is provided (age determines room eligibility, e.g., Baby rooms) // - Rendered as static text when only one room type is selected (auto-assigned by RoomAssignmentService) $this->fieldStateConditions['assignedRoomId'] = [ 'hidden' => CompositeCondition::not($dateOfBirthProvidedCondition), 'static_text' => new SingleRoomTypeCondition(), ]; // Voucher fields - hidden for inquiry bookings (status='A') // Vouchers cannot be redeemed or submitted for inquiry bookings $finalBookingOnlyCondition = new FinalBookingOnlyCondition(); $this->fieldStateConditions['purchaseVoucherCode'] = [ 'hidden' => CompositeCondition::not($finalBookingOnlyCondition), ]; $this->fieldStateConditions['promoVoucherCode'] = [ 'hidden' => CompositeCondition::not($finalBookingOnlyCondition), ]; // Example field state conditions would be registered here // For demonstration purposes, here are some example patterns: // Example 1: Make assignedRoomId readonly for participants under 18 // $this->fieldStateConditions['assignedRoomId'] = [ // 'readonly' => new AgeRangeCondition(null, 17), // ]; // Example 2: Disable service selection if no room is assigned // $this->fieldStateConditions['serviceSelection'] = [ // 'disabled' => FieldValueCondition::isEmpty('assignedRoomId'), // ]; // Example 3: Complex condition with multiple criteria // $this->fieldStateConditions['advancedOptions'] = [ // 'hidden' => CompositeCondition::or( // new AgeRangeCondition(null, 15), // FieldValueCondition::equals('userType', 'basic') // ), // ]; } }