Files
myep/src/Form/ParticipantFieldHandler/Condition/AgeRangeCondition.php
T
2025-07-24 11:12:06 +02:00

140 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Form\ParticipantFieldHandler\Condition;
use App\Form\Model\BookingDtoInterface;
/**
* Condition that evaluates participant age against specified range criteria.
*
* This condition checks if a participant's age (calculated from their date of birth)
* falls within, outside, or meets specific age criteria. It's commonly used to
* enable/disable fields based on age restrictions for services, legal requirements,
* or business rules.
*
* Age Calculation:
* - Uses participant's dateOfBirth property to calculate current age
* - Handles null dates gracefully (condition fails if no birth date)
* - Age is calculated as of today's date when condition is evaluated
*
* Range Types:
* - Minimum age: participant must be at least X years old
* - Maximum age: participant must be under X years old
* - Age range: participant must be between X and Y years old
* - Exact age: participant must be exactly X years old
*/
class AgeRangeCondition implements FieldConditionInterface
{
/**
* Creates a new age range condition.
*
* @param int|null $minAge Minimum required age (inclusive), null for no minimum
* @param int|null $maxAge Maximum allowed age (inclusive), null for no maximum
*/
public function __construct(
private readonly ?int $minAge = null,
private readonly ?int $maxAge = null,
) {
if (null === $minAge && null === $maxAge) {
throw new \InvalidArgumentException('At least one of minAge or maxAge must be specified');
}
if (null !== $minAge && null !== $maxAge && $minAge > $maxAge) {
throw new \InvalidArgumentException('Minimum age cannot be greater than maximum age');
}
}
/**
* Evaluates if the participant's age meets the specified criteria.
*
* Calculates the participant's current age from their date of birth and
* checks if it falls within the configured age range. Returns false if
* the participant has no date of birth set.
*
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
* @param int $participantIndex The index of the participant being evaluated
* @param array<string, mixed> $formData Current form data (unused for age conditions)
*
* @return bool True if the participant's age meets the criteria, false otherwise
*/
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
{
$participant = $bookingDto->getParticipant($participantIndex);
if (null === $participant || null === $participant->dateOfBirth) {
return false;
}
$age = $this->calculateAge($participant->dateOfBirth);
// Check minimum age requirement
if (null !== $this->minAge && $age < $this->minAge) {
return false;
}
// Check maximum age requirement
if (null !== $this->maxAge && $age > $this->maxAge) {
return false;
}
return true;
}
/**
* Returns field names that affect age calculation.
*
* The age condition depends on the participant's date of birth field.
* When this field changes, any conditions based on age should be re-evaluated.
*
* @return string[] Array containing 'dateOfBirth' field name
*/
public function getDependentFields(): array
{
return ['dateOfBirth'];
}
/**
* Returns a human-readable description of the age criteria.
*
* Generates a descriptive string explaining the age requirements,
* useful for debugging and understanding condition logic.
*
* @return string Description of the age range criteria
*/
public function getDescription(): string
{
if (null !== $this->minAge && null !== $this->maxAge) {
if ($this->minAge === $this->maxAge) {
return sprintf('Participant must be exactly %d years old', $this->minAge);
}
return sprintf('Participant must be between %d and %d years old', $this->minAge, $this->maxAge);
}
if (null !== $this->minAge) {
return sprintf('Participant must be at least %d years old', $this->minAge);
}
return sprintf('Participant must be under %d years old', $this->maxAge + 1);
}
/**
* Calculates age in years from a date of birth.
*
* Uses DateTimeImmutable to ensure immutable date calculations and
* handles the calculation accurately accounting for leap years and
* exact birth date anniversaries.
*
* @param \DateTimeImmutable $dateOfBirth The participant's date of birth
*
* @return int The calculated age in complete years
*/
private function calculateAge(\DateTimeImmutable $dateOfBirth): int
{
$today = new \DateTimeImmutable();
return (int) $dateOfBirth->diff($today)->y;
}
}