wip: field states and dependencies

This commit is contained in:
Björn Fromme
2026-03-16 11:59:09 +01:00
parent cd7a724e62
commit d0d6844fbd
10 changed files with 1302 additions and 22 deletions
+159
View File
@@ -0,0 +1,159 @@
# Universal Conditional Field State System
This system provides a flexible architecture for implementing conditional field states (readonly, disabled, hidden) based on participant data and interdependent field values.
## Architecture Overview
The system consists of several key components:
1. **FieldConditionInterface** - Defines the contract for condition evaluation
2. **Concrete Conditions** - Implement specific business logic (age ranges, field values, etc.)
3. **CompositeCondition** - Combines conditions with AND/OR/NOT logic
4. **FieldStateProviderInterface** - Manages field state calculation
5. **ParticipantFieldOptionsProvider** - Enhanced to support state conditions
6. **Form Integration** - Applied in BookingCreateParticipantType
## Usage Examples
### Basic Age-Based Condition
```php
// Make a field readonly for participants under 18
$this->fieldStateConditions['serviceSelection'] = [
'readonly' => new AgeRangeCondition(null, 17),
];
```
### Field Dependency Condition
```php
// Disable field if room is not assigned
$this->fieldStateConditions['mealPreference'] = [
'disabled' => FieldValueCondition::empty('assignedRoomId'),
];
```
### Complex Composite Condition
```php
// Hide field for young participants OR if basic service is selected
$this->fieldStateConditions['advancedOptions'] = [
'hidden' => CompositeCondition::or(
new AgeRangeCondition(null, 15),
FieldValueCondition::equals('serviceType', 'basic')
),
];
```
### Multiple State Conditions
```php
// Field with multiple conditional states
$this->fieldStateConditions['specialServices'] = [
'readonly' => new AgeRangeCondition(null, 17),
'required' => FieldValueCondition::equals('roomType', 'premium'),
'disabled' => CompositeCondition::and(
FieldValueCondition::empty('assignedRoomId'),
FieldValueCondition::notEquals('participantType', 'staff')
),
];
```
## Available Conditions
### AgeRangeCondition
- `new AgeRangeCondition(18, null)` - At least 18 years old
- `new AgeRangeCondition(null, 17)` - Under 18 years old
- `new AgeRangeCondition(18, 65)` - Between 18 and 65 years old
### FieldValueCondition
- `FieldValueCondition::equals('field', 'value')` - Field equals specific value
- `FieldValueCondition::notEquals('field', 'value')` - Field does not equal value
- `FieldValueCondition::in('field', ['a', 'b'])` - Field value is in array
- `FieldValueCondition::empty('field')` - Field is empty or null
- `FieldValueCondition::isNotEmpty('field')` - Field has a value
### CompositeCondition
- `CompositeCondition::and($cond1, $cond2)` - All conditions must be true
- `CompositeCondition::or($cond1, $cond2)` - At least one condition must be true
- `CompositeCondition::not($condition)` - Inverts condition result
## State Types
- **readonly** - Field is visible but not editable
- **disabled** - Field interaction is disabled
- **required** - Field becomes mandatory
- **hidden** - Field is not displayed (via CSS display: none)
## Adding New Conditions
To register field state conditions, add them to the `registerFieldStateConditions()` method in `ParticipantFieldOptionsProvider`:
```php
private function registerFieldStateConditions(): void
{
// Age-based readonly state
$this->fieldStateConditions['assignedRoomId'] = [
'readonly' => new AgeRangeCondition(null, 17),
];
// Field dependency
$this->fieldStateConditions['mealPreference'] = [
'disabled' => FieldValueCondition::empty('assignedRoomId'),
];
// Complex business logic
$this->fieldStateConditions['advancedServices'] = [
'hidden' => CompositeCondition::or(
new AgeRangeCondition(null, 15),
FieldValueCondition::equals('membershipLevel', 'basic')
),
'required' => FieldValueCondition::equals('roomType', 'suite'),
];
}
```
## Performance Considerations
- Conditions use lazy evaluation and short-circuit logic
- Field state calculations are cached during form processing
- Dependency tracking prevents unnecessary re-evaluations
- Bulk state calculation optimizes multiple field updates
## Integration with HTMX
The system supports real-time field state updates:
1. Field changes trigger dependency re-evaluation
2. State modifications are applied via form rebuilding
3. HTMX can update field states without full page refresh
4. Dependency tracking ensures only affected fields are updated
## Extending the System
### Custom Conditions
Create new condition classes implementing `FieldConditionInterface`:
```php
class CustomBusinessRuleCondition implements FieldConditionInterface
{
public function evaluate(BookingCreateDto $bookingDto, int $participantIndex, array $formData): bool
{
// Custom business logic here
return true;
}
public function getDependentFields(): array
{
return ['fieldThatTriggersThisCondition'];
}
public function getDescription(): string
{
return 'Custom business rule description';
}
}
```
This system provides a powerful, maintainable foundation for complex conditional field behavior while maintaining clean separation of concerns and extensibility.