wip: refactor forms

This commit is contained in:
Björn Fromme
2026-03-16 11:59:09 +01:00
parent 1f5877460b
commit cd7a724e62
9 changed files with 644 additions and 76 deletions
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
namespace App\Form\Service;
use App\Form\Model\BookingCreateDto;
/**
* Provides dynamic field options for participant form fields.
*
* This service acts as a field option provider registry for the participant form
* system. It generates context-aware Symfony form field options based on the
* overall booking context and individual participant data.
*
* Key Responsibilities:
* - Manages field option providers for dynamic fields
* - Provides context-aware field configurations
* - Handles interdependent field relationships
* - Supports extensible field option generation
*
* The provider uses a callable pattern where each field has a function
* that generates the appropriate Symfony form options based on the current
* booking and participant state.
*/
class ParticipantFieldOptionsProvider
{
/** @var array<string, callable> Field option providers indexed by field name */
private array $fieldOptionProviders = [];
/**
* Initializes the provider with required dependencies.
*
* The provider automatically registers all field option providers
* during construction to ensure they're available for form building.
* This approach keeps all field configuration logic centralized and
* makes it easy to add new dynamic fields.
*
* @param ParticipantRoomChoiceLoaderFactory $roomChoiceLoaderFactory Factory for creating room choice loaders
*/
public function __construct(
private readonly ParticipantRoomChoiceLoaderFactory $roomChoiceLoaderFactory,
) {
$this->registerFieldOptionProviders();
}
/**
* Retrieves form field options for a specified dynamic field.
*
* This is the main entry point for getting field configurations. It looks up
* the appropriate option provider for the field and executes it with the
* current booking and participant context to generate dynamic field options.
*
* The returned array contains Symfony form field options such as:
* - 'label' - The field label
* - 'placeholder' - Placeholder text
* - 'choices' - Available choices for choice fields
* - 'choice_loader' - Dynamic choice loader for complex choices
* - 'disabled' - Whether the field should be disabled
* - 'required' - Whether the field is required
*
* @param string $fieldName The name of the field to configure
* @param BookingCreateDto $bookingDto The current booking data for context
* @param int $participantIndex The index of the participant being configured
*
* @return array<string, mixed> Symfony form field options, or empty array if field not supported
*/
public function getFieldOptions(string $fieldName, BookingCreateDto $bookingDto, int $participantIndex): array
{
// Check if we have a provider for this field
if (!isset($this->fieldOptionProviders[$fieldName])) {
return [];
}
// Execute the provider with current context to generate dynamic options
return $this->fieldOptionProviders[$fieldName]($bookingDto, $participantIndex);
}
/**
* Checks whether a field has option provider support.
*
* This method allows form builders to determine if a field can be
* dynamically configured by this service. It's useful for deciding
* whether to use static field options or dynamic configuration.
*
* @param string $fieldName The name of the field to check
*
* @return bool True if the field has registered option providers, false otherwise
*/
public function hasFieldOptions(string $fieldName): bool
{
return isset($this->fieldOptionProviders[$fieldName]);
}
/**
* Registers all field option providers during service initialization.
*
* This method defines the option generation logic for each supported dynamic field.
* Each provider is a callable that receives the booking DTO and participant
* index and returns appropriate Symfony form field options.
*
* Adding new fields:
* To add support for a new dynamic field, simply add a new provider here:
*
* $this->fieldOptionProviders['newField'] = fn($bookingDto, $participantIndex) => [
* 'label' => 'New Field Label',
* 'choices' => $this->generateChoicesFor($bookingDto, $participantIndex),
* ];
*
* Provider Pattern Benefits:
* - Lazy evaluation (options only generated when needed)
* - Context-aware configuration
* - Easy to test individual field logic
* - Supports complex interdependencies
*/
private function registerFieldOptionProviders(): void
{
// Room assignment field provider
$this->fieldOptionProviders['assignedRoomId'] = fn (BookingCreateDto $bookingDto, int $participantIndex) => [
'label' => 'Zimmer',
'placeholder' => 'Bitte wählen',
// Use factory to create context-aware choice loader that:
// - Shows only available rooms for this participant
// - Excludes rooms already assigned to other participants
// - Respects room capacity and booking constraints
'choice_loader' => $this->roomChoiceLoaderFactory->create(
$bookingDto->participants,
$bookingDto->getSelectedRooms(),
$participantIndex
),
];
// Future field providers would be added here, for example:
//
// $this->fieldOptionProviders['mealPreference'] = fn($bookingDto, $participantIndex) => [
// 'label' => 'Meal Preference',
// 'choices' => [
// 'Standard' => 'standard',
// 'Vegetarian' => 'vegetarian',
// 'Vegan' => 'vegan',
// ],
// // Could depend on room assignment or other factors
// 'disabled' => !$this->hasMealOptions($bookingDto, $participantIndex),
// ];
}
}