feat: improved handling of fields to be rendered as static text
This commit is contained in:
@@ -31,9 +31,15 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
/**
|
||||
* Determines whether a field should be included in the form at all.
|
||||
*
|
||||
* Fields with 'hidden' state conditions should not be added to the form
|
||||
* rather than being hidden with CSS. This method evaluates the hidden
|
||||
* condition independently to allow early field exclusion.
|
||||
* 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)
|
||||
@@ -44,13 +50,57 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
*/
|
||||
public function shouldIncludeField(string $fieldName, BookingDto $bookingDto, int $participantIndex, array $formData = []): bool
|
||||
{
|
||||
if (false === isset($this->fieldStateConditions[$fieldName]['hidden'])) {
|
||||
return true; // No hidden condition means field should be included
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
$hiddenCondition = $this->fieldStateConditions[$fieldName]['hidden'];
|
||||
// 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 !$hiddenCondition->evaluate($bookingDto, $participantIndex, $formData);
|
||||
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<string, mixed> $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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,10 +108,17 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
*
|
||||
* Evaluates all configured conditions for a field and returns the appropriate
|
||||
* state modifications. State conditions are organized by state type (readonly,
|
||||
* disabled, etc.) and evaluated independently.
|
||||
* disabled, required, etc.) and evaluated independently.
|
||||
*
|
||||
* Note: This method no longer handles 'hidden' state as fields should be
|
||||
* excluded from the form entirely rather than hidden with CSS.
|
||||
* 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)
|
||||
@@ -91,6 +148,8 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
||||
case 'required':
|
||||
$stateModifications['required'] = true;
|
||||
break;
|
||||
// Note: 'static_text' and 'hidden' are intentionally not handled here
|
||||
// They are checked via shouldRenderAsStaticText() and shouldIncludeField()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,12 @@ use App\Service\ParticipantEligibilityService;
|
||||
/**
|
||||
* Field state provider for the booking create workflow.
|
||||
*
|
||||
* This service calculates dynamic field states (readonly, disabled, etc.)
|
||||
* 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
|
||||
@@ -75,58 +76,58 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
$skiPassCondition = new SkiPassSelectionCondition();
|
||||
|
||||
// Authenticated user personal data protection
|
||||
// Hide personal data fields for participants linked to BPN accounts (prevents duplicate records)
|
||||
// Render personal data fields as static text for participants linked to BPN accounts (prevents duplicate records)
|
||||
$authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition();
|
||||
|
||||
$this->fieldStateConditions['firstName'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['lastName'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['gender'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['nationality'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['dateOfBirth'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['email'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
// Mobile field: hidden for authenticated users, required for guest applicants
|
||||
// Mobile field: static text for authenticated users, required for guest applicants
|
||||
$this->fieldStateConditions['mobile'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
'required' => new ApplicantCondition(),
|
||||
];
|
||||
|
||||
// Address subfields - must be hidden to prevent creating duplicate BPN records
|
||||
// Address subfields - must render as static text to prevent creating duplicate BPN records
|
||||
$this->fieldStateConditions['address.street'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.postCode'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.city'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.country'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.district'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
// Note: Body dimensions (height, weight, shoeSize) are NOT hidden for authenticated users
|
||||
@@ -255,7 +256,9 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
];
|
||||
|
||||
// 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(),
|
||||
];
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
$pickupsMutabilityCondition = new PickupsMutabilityCondition();
|
||||
|
||||
// Authenticated user personal data protection
|
||||
// Hide personal data fields for participants linked to BPN accounts (prevents duplicate records)
|
||||
// Render personal data fields as static text for participants linked to BPN accounts (prevents duplicate records)
|
||||
$authenticatedUserCondition = new AuthenticatedUserPersonalDataCondition();
|
||||
|
||||
// Make all personal data fields readonly if participant not mutable
|
||||
// OR hidden if participant is linked to BPN account (authenticated user)
|
||||
// OR static text if participant is linked to BPN account (authenticated user)
|
||||
// Note: First participant is now treated as independent from applicant and can be edited
|
||||
$personalDataFields = [
|
||||
'firstName',
|
||||
@@ -62,36 +62,36 @@ class EditFieldStateProvider extends AbstractFieldStateProvider
|
||||
];
|
||||
foreach ($personalDataFields as $field) {
|
||||
$this->fieldStateConditions[$field] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
'readonly' => CompositeCondition::not(new MutabilityCondition()),
|
||||
];
|
||||
}
|
||||
|
||||
// Address fields - hidden for authenticated users, readonly if participant not mutable
|
||||
// Address fields - static text for authenticated users, readonly if participant not mutable
|
||||
$this->fieldStateConditions['address'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
'readonly' => CompositeCondition::not(new MutabilityCondition()),
|
||||
];
|
||||
|
||||
// Address subfields - must be hidden to prevent creating duplicate BPN records
|
||||
// Address subfields - must render as static text to prevent creating duplicate BPN records
|
||||
$this->fieldStateConditions['address.street'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.postCode'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.city'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.country'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
$this->fieldStateConditions['address.district'] = [
|
||||
'hidden' => $authenticatedUserCondition,
|
||||
'static_text' => $authenticatedUserCondition,
|
||||
];
|
||||
|
||||
// Conditional visibility for service fields (same as create flow)
|
||||
|
||||
Reference in New Issue
Block a user