'Y-m-d'])] public ?\DateTimeImmutable $travelDateFrom = null; #[Groups(['api:single', 'api:list'])] #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] public ?\DateTimeImmutable $travelDateTo = null; #[Groups(['api:single', 'api:list'])] #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] public ?\DateTimeImmutable $bookingDateFrom = null; #[Groups(['api:single', 'api:list'])] #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])] public ?\DateTimeImmutable $bookingDateTo = null; #[Groups(['api:single', 'api:list'])] public ?int $ageFrom = null; #[Groups(['api:single', 'api:list'])] public ?int $ageTo = null; #[Groups(['api:single', 'api:list'])] public ?float $travelPriceFrom = null; #[Groups(['api:single', 'api:list'])] public ?float $travelPriceTo = null; #[Groups(['api:single', 'api:list'])] public ?int $travelDurationFrom = null; #[Groups(['api:single', 'api:list'])] public ?int $travelDurationTo = null; #[Groups(['api:single', 'api:list'])] public ?string $urlInfo = null; #[Groups(['api:single', 'api:list'])] public ?string $urlProductInfo = null; #[Groups(['api:single', 'api:list'])] public ?string $urlTerms = null; /** * @var array Insurance products contained in this package (only for packages) */ #[Groups(['api:single', 'api:list'])] public array $containedInsurances = []; /** * @var array IDs of insurance products contained in this package (only for packages) */ #[Groups(['api:single', 'api:list'])] public array $containedInsuranceIds = []; /** * @var array Participant indices assigned to this insurance (booking data only) */ public array $mapping = []; /** * @var array Individual prices per participant index (booking data only) */ public array $individualPrice = []; /** * Returns the subType, with virtual 'PKG' subType for insurance packages. * * @return string|null The subType (RRV, PAK, OHN, PKG) or null */ public function getSubType(): ?string { if (true === $this->package) { return 'PKG'; // Virtual subType for all insurance packages } return $this->subType; } /** * Returns all info URLs from this insurance or its contained insurances (for packages). * * @return array Array of info URLs */ public function getAllUrlsInfo(): array { if (true === $this->package) { return $this->collectUrlsFromContainedInsurances('urlInfo'); } return null !== $this->urlInfo ? [$this->urlInfo] : []; } /** * Returns all product info URLs from this insurance or its contained insurances (for packages). * * @return array Array of product info URLs */ public function getAllUrlsProductInfo(): array { if (true === $this->package) { return $this->collectUrlsFromContainedInsurances('urlProductInfo'); } return null !== $this->urlProductInfo ? [$this->urlProductInfo] : []; } /** * Returns all terms URLs from this insurance or its contained insurances (for packages). * * @return array Array of terms URLs */ public function getAllUrlsTerms(): array { if (true === $this->package) { return $this->collectUrlsFromContainedInsurances('urlTerms'); } return null !== $this->urlTerms ? [$this->urlTerms] : []; } /** * Collects URLs from contained insurances for a specific URL property. * * @param string $urlProperty The URL property name to collect (urlInfo, urlProductInfo, urlTerms) * * @return array Array of non-null URLs from contained insurances */ private function collectUrlsFromContainedInsurances(string $urlProperty): array { $urls = []; foreach ($this->containedInsurances as $containedInsurance) { if (null !== $containedInsurance->{$urlProperty}) { $urls[] = $containedInsurance->{$urlProperty}; } } return array_values(array_unique($urls)); } /** * Checks if this is the synthetic "no insurance" option. * * The "no insurance" option is a UI construct used to force explicit user choice. * It should be excluded from BPN API transmission. * * @return bool True if this is the "no insurance" option */ public function isNoInsurance(): bool { return self::NO_INSURANCE_ID === $this->id; } }