diff --git a/src/Form/Service/ParticipantFieldHandlerRegistry.php b/src/Form/Service/ParticipantFieldHandlerRegistry.php index cc2e72e..d375b2b 100644 --- a/src/Form/Service/ParticipantFieldHandlerRegistry.php +++ b/src/Form/Service/ParticipantFieldHandlerRegistry.php @@ -21,6 +21,32 @@ use App\Form\Service\Contract\ParticipantFieldHandlerInterface; */ class ParticipantFieldHandlerRegistry { + private const MUTABILITY_ADDITIONAL_SERVICES = 'additional_services'; + private const MUTABILITY_TRANSPORTATION = 'transportation'; + private const MUTABILITY_PICKUPS = 'pickups'; + + /** + * Maps handler names to mutability categories for edit-mode enforcement. + * + * @var array + */ + private const HANDLER_MUTABILITY_CATEGORY_MAP = [ + 'additionalServices' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'courses' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'board' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'veg' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'skiPass' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'rentals' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'rentalInsurance' => self::MUTABILITY_ADDITIONAL_SERVICES, + 'transportationOutbound' => self::MUTABILITY_TRANSPORTATION, + 'transportationInbound' => self::MUTABILITY_TRANSPORTATION, + 'parking' => self::MUTABILITY_TRANSPORTATION, + 'licensePlate' => self::MUTABILITY_TRANSPORTATION, + 'transportationDiscountReplacement' => self::MUTABILITY_TRANSPORTATION, + 'pickup' => self::MUTABILITY_PICKUPS, + 'dropOff' => self::MUTABILITY_PICKUPS, + ]; + /** @var array Registered handlers indexed by field name */ private array $handlers = []; @@ -121,6 +147,10 @@ class ParticipantFieldHandlerRegistry // Process each handler across all participants before moving to the next handler // This ensures booking-level state (like family booking detection) is accurate foreach ($sortedHandlerNames as $handlerName) { + if (false === $this->isHandlerMutableForBooking($handlerName, $bookingDto)) { + continue; + } + $handler = $this->handlers[$handlerName]; // Apply this handler to all participants @@ -158,6 +188,10 @@ class ParticipantFieldHandlerRegistry // Apply each handler to the specified participant foreach ($sortedHandlerNames as $handlerName) { + if (false === $this->isHandlerMutableForBooking($handlerName, $bookingDto)) { + continue; + } + $handler = $this->handlers[$handlerName]; // Let each handler decide if it should process this participant's data @@ -224,8 +258,11 @@ class ParticipantFieldHandlerRegistry 'courses', 'additionalServices', 'board', + 'veg', 'pickup', 'dropOff', + 'parking', + 'licensePlate', ]; foreach ($serviceFields as $fieldName) { @@ -521,6 +558,25 @@ class ParticipantFieldHandlerRegistry return $this->convertSingleValueToSubmittedFormat($dtoValue); } + /** + * Checks whether a field handler is allowed to run based on edit-mode mutability flags. + */ + private function isHandlerMutableForBooking(string $handlerName, BookingDto $bookingDto): bool + { + if (BookingDto::MODE_CREATE === $bookingDto->getMode()) { + return true; + } + + $mutabilityCategory = self::HANDLER_MUTABILITY_CATEGORY_MAP[$handlerName] ?? null; + + return match ($mutabilityCategory) { + self::MUTABILITY_ADDITIONAL_SERVICES => $bookingDto->travel->additionalServicesMutable, + self::MUTABILITY_TRANSPORTATION => $bookingDto->travel->transportationServicesMutable, + self::MUTABILITY_PICKUPS => $bookingDto->travel->pickupsMutable, + default => true, + }; + } + /** * Converts a single DTO value to submitted form format. * diff --git a/templates/booking/_form_theme.html.twig b/templates/booking/_form_theme.html.twig index 0c4264a..bc0d411 100644 --- a/templates/booking/_form_theme.html.twig +++ b/templates/booking/_form_theme.html.twig @@ -41,7 +41,8 @@ {%- for child in form %} {% set choiceData = form.vars.choices[loop.index0] is defined ? form.vars.choices[loop.index0].data : null %} {% set description = child.vars.attr['data-description']|default(null) %} - {% set isReadonly = child.vars.attr.readonly is defined %} + {% set fieldReadonly = form.vars.attr.readonly is defined %} + {% set isReadonly = fieldReadonly or child.vars.attr.readonly is defined %} {%- set child_attr = {} -%} {%- if attr['hx-trigger'] is defined -%} {%- set child_attr = { @@ -51,6 +52,9 @@ 'hx-swap': attr['hx-swap'] } -%} {%- endif -%} + {%- if fieldReadonly and child.vars.attr.readonly is not defined -%} + {%- set child_attr = child_attr|merge({'readonly': true}) -%} + {%- endif -%}
{{- child.vars.label -}}
diff --git a/templates/forms.html.twig b/templates/forms.html.twig index 5f9da02..54dff11 100644 --- a/templates/forms.html.twig +++ b/templates/forms.html.twig @@ -205,7 +205,9 @@ {%- if attr.readonly is defined %}
- {{ parent() }} +
+ {{ parent() }} +
{% else %} {{ parent() }} @@ -223,6 +225,9 @@ 'hx-swap': attr['hx-swap'] }) -%} {%- endif -%} + {%- if attr.readonly is defined and child.vars.attr.readonly is not defined -%} + {%- set child_attr = child_attr|merge({'readonly': true}) -%} + {%- endif -%} {%- if child.vars.attr['data-tooltip'] is defined -%} {%- set child_attr = child_attr|merge({'data-tooltip': child.vars.attr['data-tooltip']}) -%} {%- endif -%} @@ -358,4 +363,3 @@ {% endblock %} -