229 lines
9.8 KiB
PHP
229 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Form\Service\Abstract;
|
|
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Service\Contract\FieldConditionInterface;
|
|
use App\Form\Service\Contract\FieldStateProviderInterface;
|
|
use App\Form\Service\Trait\FormTraversalTrait;
|
|
|
|
/**
|
|
* Abstract base class for field state providers.
|
|
*
|
|
* This class contains common field state evaluation logic shared between
|
|
* different field state provider implementations. It provides the core
|
|
* functionality for evaluating field conditions and applying state modifications.
|
|
*/
|
|
abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
|
|
{
|
|
use FormTraversalTrait;
|
|
|
|
/** @var array<string, array<string, FieldConditionInterface>> 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<string, mixed> $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<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);
|
|
}
|
|
|
|
/**
|
|
* 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<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, 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<string, mixed> $formData Current form data for condition evaluation
|
|
*
|
|
* @return array<string, array<string, mixed>> 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;
|
|
}
|