Files
myep/src/Form/Service/CreateFieldStateProvider.php
T

292 lines
12 KiB
PHP

<?php
declare(strict_types=1);
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\AuthenticatedUserPersonalDataCondition;
use App\Form\Service\Condition\BookingEligibilityCondition;
use App\Form\Service\Condition\BulkInsuranceBookingCondition;
use App\Form\Service\Condition\CompositeCondition;
use App\Form\Service\Condition\DateOfBirthProvidedCondition;
use App\Form\Service\Condition\FieldValueCondition;
use App\Form\Service\Condition\RentalSelectionCondition;
use App\Form\Service\Condition\RoomSelectionCondition;
use App\Form\Service\Condition\ServiceSubTypeCondition;
use App\Form\Service\Condition\SingleRoomTypeCondition;
use App\Form\Service\Condition\SkiPassSelectionCondition;
use App\Service\ParticipantEligibilityService;
/**
* Field state provider for the booking create workflow.
*
* This service calculates dynamic field states (readonly, disabled, static_text, etc.)
* for participant fields in the create flow. It extends the common field
* state functionality provided by AbstractFieldStateProvider.
*
* Current field state conditions:
* - Personal data fields render as static text for authenticated users (prevents duplicate BPN records)
* - Body dimension fields are hidden unless rental services are selected
* - Age-dependent service fields are hidden until birth date is provided
* - Transportation pickup fields are hidden by default, shown only when transportation type is BUS
* - Parking field is hidden by default, shown only when outbound transportation is PKW
* - All service fields are hidden for ineligible participants (no skipasses available for their age)
*/
class CreateFieldStateProvider extends AbstractFieldStateProvider
{
public function __construct(
private readonly ParticipantEligibilityService $participantEligibilityService,
) {
parent::__construct();
}
/**
* Registers field state conditions for the create workflow.
*
* This method defines the conditional logic for field states in the
* booking creation process. Each field can have multiple state conditions
* (readonly, disabled, hidden, required) that are evaluated independently.
*
* Adding new field state conditions:
* To add conditional state logic for a field, register conditions here:
*
* $this->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();
// Authenticated user personal data protection
// Render personal data fields as static text for participants linked to BPN accounts (prevents duplicate records)
$authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition();
$this->fieldStateConditions['firstName'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['lastName'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['gender'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['nationality'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['dateOfBirth'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['email'] = [
'static_text' => $authenticatedUserCondition,
];
// Mobile field: static text for authenticated users, required for guest applicants
$this->fieldStateConditions['mobile'] = [
'static_text' => $authenticatedUserCondition,
'required' => new ApplicantCondition(),
];
// Address subfields - must render as static text to prevent creating duplicate BPN records
$this->fieldStateConditions['address.street'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.postCode'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.city'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.country'] = [
'static_text' => $authenticatedUserCondition,
];
$this->fieldStateConditions['address.district'] = [
'static_text' => $authenticatedUserCondition,
];
// 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();
// 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 participant is ineligible OR when no date of birth
$this->fieldStateConditions['skiPass'] = [
'hidden' => CompositeCondition::or(
CompositeCondition::not($dateOfBirthProvidedCondition),
$bookingEligibilityCondition
),
];
// 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 applicant (index 0) and when insurance field is visible
$this->fieldStateConditions['bulkInsuranceBooking'] = [
'hidden' => CompositeCondition::or(
CompositeCondition::not($dateOfBirthProvidedCondition), // Hide until date of birth provided
CompositeCondition::not(new ApplicantCondition()) // Hide for non-applicants
),
];
// 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 (hidden by default)
// Parking is offered at holiday destination for those arriving by car
$this->fieldStateConditions['parking'] = [
'hidden' => CompositeCondition::not(
ServiceSubTypeCondition::equals('transportationOutbound', DirectionMapper::SUBTYPE_CAR_API)
),
];
// Show rental insurance only when rental services are selected (hidden by default)
$this->fieldStateConditions['rentalInsurance'] = [
'hidden' => CompositeCondition::not($rentalCondition),
];
// Show license plate only when parking is selected (hidden by default)
$this->fieldStateConditions['licensePlate'] = [
'hidden' => FieldValueCondition::equals('parking', false),
];
// Make address field required for applicant (participant index 0)
// Also render as static text for authenticated users to prevent duplicate BPN records
$this->fieldStateConditions['address'] = [
'static_text' => $authenticatedUserCondition,
'required' => new ApplicantCondition(),
];
// Render assignedRoomId field as static text when only one room type is selected (auto-assigned by RoomAssignmentService)
$this->fieldStateConditions['assignedRoomId'] = [
'static_text' => new SingleRoomTypeCondition(),
];
// 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')
// ),
// ];
}
}