> Field state conditions indexed by field name and state type */ protected array $fieldStateConditions = []; public function __construct() { $this->registerFieldStateConditions(); } /** * Determines whether a field should be included in the form at all. * * Fields with 'hidden' or 'static_text' state conditions should not be added to the form structure. * Both states exclude fields from the form to prevent form_rest() from rendering them. * * The distinction between 'hidden' and 'static_text': * - 'hidden': Field completely excluded from form (not rendered at all) * - 'static_text': Field excluded from form but template renders the value as static text * * Templates must check shouldRenderAsStaticText() to determine if they should * display the field's value as static text when the field is not in the form. * * @param string $fieldName The name of the field to evaluate * @param BookingDto $bookingDto The current booking data for context (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 the field should be included in the form, false if it should be excluded */ public function shouldIncludeField(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): bool { // Check if field has 'hidden' condition if (isset($this->fieldStateConditions[$fieldName]['hidden'])) { $hiddenCondition = $this->fieldStateConditions[$fieldName]['hidden']; if ($hiddenCondition->evaluate($bookingDto, $participantIndex, $formData)) { return false; // Field is hidden, exclude from form } } // Check if field has 'static_text' condition if (isset($this->fieldStateConditions[$fieldName]['static_text'])) { $staticTextCondition = $this->fieldStateConditions[$fieldName]['static_text']; if ($staticTextCondition->evaluate($bookingDto, $participantIndex, $formData)) { return false; // Field should render as static text, exclude from form } } return true; // No conditions met, include field in form } /** * Determines whether a field should be rendered as static text instead of a form input. * * Fields with 'static_text' state are included in the form structure but should be * displayed as read-only static text by the template. This is used for: * - Personal data of authenticated users (prevents creating duplicate BPN records) * - Fields locked by BPN's mutability rules (aenderungmoeglich flag) * - Any other scenario where data should be visible but not editable * * Example usage in templates: * {% if is_static_text(form.firstName, bookingDto, participantIndex) %} * {{ render_static_field(form.firstName, participant.firstName) }} * {% else %} * {{ form_row(form.firstName) }} * {% endif %} * * @param string $fieldName The name of the field to evaluate * @param BookingDto $bookingDto The current booking data for context (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 the field should be rendered as static text, false if it should be a normal form input */ public function shouldRenderAsStaticText(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): bool { if (false === isset($this->fieldStateConditions[$fieldName]['static_text'])) { return false; // No static_text condition means field should render normally } $staticTextCondition = $this->fieldStateConditions[$fieldName]['static_text']; return $staticTextCondition->evaluate($bookingDto, $participantIndex, $formData); } /** * Calculates the dynamic state for a specified field. * * Evaluates all configured conditions for a field and returns the appropriate * state modifications. State conditions are organized by state type (readonly, * disabled, required, etc.) and evaluated independently. * * Supported state types: * - 'readonly': Adds readonly HTML attribute to the field * - 'disabled': Disables the field completely * - 'required': Makes the field mandatory * - 'static_text': Field should be rendered as static text (checked separately via shouldRenderAsStaticText()) * - 'hidden': Field excluded from form entirely (checked via shouldIncludeField()) * * Note: 'static_text' and 'hidden' states are not returned by this method as they * affect form structure/template rendering rather than Symfony form field options. * * @param string $fieldName The name of the field to evaluate * @param BookingDto $bookingDto The current booking data for context (create or edit) * @param int $participantIndex The index of the participant being evaluated * @param array $formData Current form data for condition evaluation * * @return array Symfony form field options for state modifications */ public function getFieldState(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): array { if (false === isset($this->fieldStateConditions[$fieldName])) { return []; } $stateModifications = []; $attributes = []; foreach ($this->fieldStateConditions[$fieldName] as $stateType => $condition) { if ($condition->evaluate($bookingDto, $participantIndex, $formData)) { switch ($stateType) { case 'readonly': $attributes['readonly'] = true; break; case 'disabled': $stateModifications['disabled'] = true; break; case 'required': $stateModifications['required'] = true; break; // Note: 'static_text' and 'hidden' are intentionally not handled here // They are checked via shouldRenderAsStaticText() and shouldIncludeField() } } } if ([] !== $attributes) { $stateModifications['attr'] = $attributes; } return $stateModifications; } /** * Checks whether a field has state conditions configured. * * @param string $fieldName The name of the field to check * * @return bool True if the field has state conditions configured */ public function hasStateConditions(string $fieldName): bool { return isset($this->fieldStateConditions[$fieldName]) && [] !== $this->fieldStateConditions[$fieldName]; } /** * Returns field names that trigger state re-evaluation for a given field. * * @param string $fieldName The name of the field to get dependencies for * * @return string[] Array of field names that affect the specified field's state */ public function getFieldStateDependencies(string $fieldName): array { if (!isset($this->fieldStateConditions[$fieldName])) { return []; } $dependencies = []; foreach ($this->fieldStateConditions[$fieldName] as $condition) { $dependencies = array_merge($dependencies, $condition->getDependentFields()); } return array_unique($dependencies); } /** * Calculates field states for all configured fields at once. * * @param BookingDto $bookingDto The current booking data for context (create or edit) * @param int $participantIndex The index of the participant being evaluated * @param array $formData Current form data for condition evaluation * * @return array> Field states indexed by field name */ public function getAllFieldStates(BookingDto $bookingDto, int $participantIndex, array $formData = []): array { $allStates = []; foreach (array_keys($this->fieldStateConditions) as $fieldName) { $fieldState = $this->getFieldState($fieldName, $bookingDto, $participantIndex, $formData); if ([] !== $fieldState) { $allStates[$fieldName] = $fieldState; } } return $allStates; } /** * Registers field state conditions during service initialization. * * This method must be implemented by concrete classes to define their * specific field state conditions. */ abstract protected function registerFieldStateConditions(): void; }