207 lines
7.4 KiB
PHP
207 lines
7.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Form\Service;
|
|
|
|
use App\BusProNet\Model\Service;
|
|
use App\Form\Model\BookingDto;
|
|
|
|
/**
|
|
* Evaluates service availability based on participant age constraints.
|
|
*
|
|
* This service handles the business logic of determining whether a service
|
|
* is available to a specific participant based on their age or birth year.
|
|
* It supports both absolute age constraints and birth year range constraints.
|
|
*/
|
|
class ServiceAgeEvaluator
|
|
{
|
|
/**
|
|
* Determines if this evaluator can process the given service.
|
|
*
|
|
* @param Service $service The service to evaluate
|
|
*
|
|
* @return bool True if the service has age constraints that can be evaluated
|
|
*/
|
|
public function canEvaluate(Service $service): bool
|
|
{
|
|
return null !== $service->ageConstraintType;
|
|
}
|
|
|
|
/**
|
|
* Evaluates if a service is available for a specific participant.
|
|
*
|
|
* Checks the participant's age or birth year against the service's
|
|
* constraint requirements. Returns false if the participant doesn't
|
|
* meet the age requirements or if no birth date is provided.
|
|
*
|
|
* Age constraints are evaluated at the travel start date to ensure
|
|
* that services are appropriately filtered based on the participant's
|
|
* age at the time of travel, not their current age.
|
|
*
|
|
* @param Service $service The service to evaluate
|
|
* @param BookingDto $bookingDto The booking containing participant data
|
|
* @param int $participantIndex The index of the participant to evaluate
|
|
*
|
|
* @return bool True if the service is available for the participant
|
|
*/
|
|
public function isServiceAvailableForParticipant(Service $service, BookingDto $bookingDto, int $participantIndex): bool
|
|
{
|
|
$participant = $bookingDto->getParticipant($participantIndex);
|
|
|
|
if (null === $participant || null === $participant->dateOfBirth) {
|
|
return false; // Cannot evaluate without birth date
|
|
}
|
|
|
|
// Use travel start date as reference for absolute age calculations
|
|
$travelStartDate = $bookingDto->travel->dateFrom;
|
|
|
|
return match ($service->ageConstraintType) {
|
|
'absolute_age' => $this->evaluateAbsoluteAge($service, $participant->dateOfBirth, $travelStartDate),
|
|
'birth_year' => $this->evaluateBirthYear($service, $participant->dateOfBirth),
|
|
'mixed' => $this->evaluateAbsoluteAge($service, $participant->dateOfBirth, $travelStartDate)
|
|
&& $this->evaluateBirthYear($service, $participant->dateOfBirth),
|
|
default => true, // No constraints or unknown type
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Evaluates absolute age constraints against participant's age at travel date.
|
|
*
|
|
* @param Service $service The service with age constraints
|
|
* @param \DateTimeImmutable $dateOfBirth The participant's date of birth
|
|
* @param \DateTimeImmutable|null $referenceDate The reference date for age calculation (travel start date)
|
|
*
|
|
* @return bool True if the participant meets the absolute age requirements
|
|
*/
|
|
private function evaluateAbsoluteAge(Service $service, \DateTimeImmutable $dateOfBirth, ?\DateTimeImmutable $referenceDate = null): bool
|
|
{
|
|
$age = $this->calculateAge($dateOfBirth, $referenceDate);
|
|
|
|
if (null !== $service->ageFrom && $age < $service->ageFrom) {
|
|
return false;
|
|
}
|
|
|
|
if (null !== $service->ageTo && $age > $service->ageTo) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Evaluates birth year constraints against participant's birth year.
|
|
*
|
|
* @param Service $service The service with birth year constraints
|
|
* @param \DateTimeImmutable $dateOfBirth The participant's date of birth
|
|
*
|
|
* @return bool True if the participant meets the birth year requirements
|
|
*/
|
|
private function evaluateBirthYear(Service $service, \DateTimeImmutable $dateOfBirth): bool
|
|
{
|
|
$birthYear = (int) $dateOfBirth->format('Y');
|
|
|
|
if (null !== $service->birthYearFrom && $birthYear < $service->birthYearFrom) {
|
|
return false;
|
|
}
|
|
|
|
if (null !== $service->birthYearTo && $birthYear > $service->birthYearTo) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Calculates age in years from a date of birth at a reference date.
|
|
*
|
|
* Uses DateTimeImmutable to ensure accurate age calculations accounting
|
|
* for leap years and exact birth date anniversaries.
|
|
*
|
|
* @param \DateTimeImmutable $dateOfBirth The participant's date of birth
|
|
* @param \DateTimeImmutable|null $referenceDate The reference date for age calculation (defaults to today)
|
|
*
|
|
* @return int The calculated age in complete years
|
|
*/
|
|
private function calculateAge(\DateTimeImmutable $dateOfBirth, ?\DateTimeImmutable $referenceDate = null): int
|
|
{
|
|
$referenceDate = $referenceDate ?? new \DateTimeImmutable();
|
|
|
|
return $dateOfBirth->diff($referenceDate)->y;
|
|
}
|
|
|
|
/**
|
|
* Gets a human-readable description of the service's age constraints.
|
|
*
|
|
* Generates descriptive text explaining the age requirements,
|
|
* useful for user interfaces and debugging.
|
|
*
|
|
* @param Service $service The service to describe
|
|
*
|
|
* @return string Description of the age constraints
|
|
*/
|
|
public function getConstraintDescription(Service $service): string
|
|
{
|
|
return match ($service->ageConstraintType) {
|
|
'absolute_age' => $this->getAbsoluteAgeDescription($service),
|
|
'birth_year' => $this->getBirthYearDescription($service),
|
|
'mixed' => sprintf('%s and %s',
|
|
$this->getAbsoluteAgeDescription($service),
|
|
$this->getBirthYearDescription($service)),
|
|
default => 'No age restrictions',
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets description for absolute age constraints.
|
|
*
|
|
* @param Service $service The service with age constraints
|
|
*
|
|
* @return string Description of absolute age requirements
|
|
*/
|
|
private function getAbsoluteAgeDescription(Service $service): string
|
|
{
|
|
if (null !== $service->ageFrom && null !== $service->ageTo) {
|
|
return sprintf('Ages %d-%d', $service->ageFrom, $service->ageTo);
|
|
}
|
|
|
|
if (null !== $service->ageFrom) {
|
|
return sprintf('Age %d+', $service->ageFrom);
|
|
}
|
|
|
|
if (null !== $service->ageTo) {
|
|
return sprintf('Age up to %d', $service->ageTo);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Gets description for birth year constraints.
|
|
*
|
|
* @param Service $service The service with birth year constraints
|
|
*
|
|
* @return string Description of birth year requirements
|
|
*/
|
|
private function getBirthYearDescription(Service $service): string
|
|
{
|
|
if (null !== $service->birthYearFrom && null !== $service->birthYearTo) {
|
|
if ($service->birthYearFrom === $service->birthYearTo) {
|
|
return sprintf('Born in %d', $service->birthYearFrom);
|
|
}
|
|
|
|
return sprintf('Born %d-%d', $service->birthYearFrom, $service->birthYearTo);
|
|
}
|
|
|
|
if (null !== $service->birthYearFrom) {
|
|
return sprintf('Born %d or later', $service->birthYearFrom);
|
|
}
|
|
|
|
if (null !== $service->birthYearTo) {
|
|
return sprintf('Born up to %d', $service->birthYearTo);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|