wip: refactor

This commit is contained in:
Björn Fromme
2025-07-24 11:12:06 +02:00
parent 64d51c01b2
commit 3f94c51bc8
21 changed files with 395 additions and 110 deletions
+126
View File
@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace App\Form\Service;
use App\Form\Model\BookingDtoInterface;
use App\Form\ParticipantFieldHandler\Condition\FieldConditionInterface;
use App\Form\ParticipantFieldHandler\Condition\MutabilityCondition;
use App\Form\ParticipantFieldHandler\Condition\ApplicantCondition;
use App\Form\ParticipantFieldHandler\Condition\CompositeCondition;
/**
* Field state provider for the booking edit workflow.
*
* This service calculates dynamic field states (readonly, disabled, etc.)
* for participant fields in the edit flow, using edit-specific conditions.
*
* It is designed to be extensible and composable, allowing reuse of
* existing condition classes and easy registration of new logic.
*/
class EditFieldStateProvider implements FieldStateProviderInterface
{
/** @var array<string, array<string, FieldConditionInterface>> */
private array $fieldStateConditions = [];
public function __construct()
{
$this->registerFieldStateConditions();
}
/**
* Registers field state conditions for the edit workflow.
*
* Add or modify conditions as needed for your domain.
*/
private function registerFieldStateConditions(): void
{
// Make all personal data fields readonly if not mutable OR if applicant
$personalDataFields = [
'firstName',
'lastName',
'dateOfBirth',
'gender',
'nationality',
'email',
'mobile',
];
foreach ($personalDataFields as $field) {
$this->fieldStateConditions[$field] = [
'readonly' => CompositeCondition::or(
new ApplicantCondition(),
new MutabilityCondition()
),
];
}
// Example: Only applicant can edit email
$this->fieldStateConditions['email']['readonly'] = new ApplicantCondition();
}
public function getFieldState(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
{
if (!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;
case 'hidden':
$attributes['style'] = ($attributes['style'] ?? '').' display: none;';
break;
}
}
}
if (!empty($attributes)) {
$stateModifications['attr'] = $attributes;
}
return $stateModifications;
}
public function hasStateConditions(string $fieldName): bool
{
return isset($this->fieldStateConditions[$fieldName]) && !empty($this->fieldStateConditions[$fieldName]);
}
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);
}
public function getAllFieldStates(BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
{
$allStates = [];
foreach (array_keys($this->fieldStateConditions) as $fieldName) {
$fieldState = $this->getFieldState($fieldName, $bookingDto, $participantIndex, $formData);
if (!empty($fieldState)) {
$allStates[$fieldName] = $fieldState;
}
}
return $allStates;
}
}
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Form\Service;
use App\Form\Model\BookingCreateDto;
use App\Form\Model\BookingDtoInterface;
/**
* Interface for providing dynamic field state based on conditions.
@@ -46,13 +46,13 @@ interface FieldStateProviderInterface
* - 'attr' => ['class' => 'conditional-field'] - Add CSS classes
*
* @param string $fieldName The name of the field to evaluate
* @param BookingCreateDto $bookingDto The current booking data for context
* @param BookingDtoInterface $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 (may include partial submissions)
*
* @return array<string, mixed> Symfony form field options for state modifications, empty if no changes needed
*/
public function getFieldState(string $fieldName, BookingCreateDto $bookingDto, int $participantIndex, array $formData = []): array;
public function getFieldState(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array;
/**
* Checks whether a field has state conditions configured.
@@ -91,11 +91,11 @@ interface FieldStateProviderInterface
* when multiple field states need to be determined simultaneously. It's
* particularly useful during form building and bulk state updates.
*
* @param BookingCreateDto $bookingDto The current booking data for context
* @param BookingDtoInterface $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 array<string, array<string, mixed>> Field states indexed by field name
*/
public function getAllFieldStates(BookingCreateDto $bookingDto, int $participantIndex, array $formData = []): array;
public function getAllFieldStates(BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array;
}
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Form\Service;
use App\Form\Model\BookingCreateDto;
use App\Form\Model\BookingDtoInterface;
use App\Form\ParticipantFieldHandler\Condition\FieldConditionInterface;
/**
@@ -64,13 +64,13 @@ class ParticipantFieldOptionsProvider implements FieldStateProviderInterface
* - '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
* @param string $fieldName The name of the field to configure
* @param BookingDtoInterface $bookingDto The current booking data for context (create or edit)
* @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
public function getFieldOptions(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex): array
{
// Check if we have a provider for this field
if (!isset($this->fieldOptionProviders[$fieldName])) {
@@ -121,7 +121,7 @@ class ParticipantFieldOptionsProvider implements FieldStateProviderInterface
private function registerFieldOptionProviders(): void
{
// Room assignment field provider
$this->fieldOptionProviders['assignedRoomId'] = fn (BookingCreateDto $bookingDto, int $participantIndex) => [
$this->fieldOptionProviders['assignedRoomId'] = fn (BookingDtoInterface $bookingDto, int $participantIndex) => [
'label' => 'Zimmer',
'placeholder' => 'Bitte wählen',
// Use factory to create context-aware choice loader that:
@@ -155,13 +155,13 @@ class ParticipantFieldOptionsProvider implements FieldStateProviderInterface
* disabled, etc.) and evaluated independently.
*
* @param string $fieldName The name of the field to evaluate
* @param BookingCreateDto $bookingDto The current booking data for context
* @param BookingDtoInterface $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 array<string, mixed> Symfony form field options for state modifications
*/
public function getFieldState(string $fieldName, BookingCreateDto $bookingDto, int $participantIndex, array $formData = []): array
public function getFieldState(string $fieldName, BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
{
if (!isset($this->fieldStateConditions[$fieldName])) {
return [];
@@ -233,13 +233,13 @@ class ParticipantFieldOptionsProvider implements FieldStateProviderInterface
/**
* Calculates field states for all configured fields at once.
*
* @param BookingCreateDto $bookingDto The current booking data for context
* @param BookingDtoInterface $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 array<string, array<string, mixed>> Field states indexed by field name
*/
public function getAllFieldStates(BookingCreateDto $bookingDto, int $participantIndex, array $formData = []): array
public function getAllFieldStates(BookingDtoInterface $bookingDto, int $participantIndex, array $formData = []): array
{
$allStates = [];