events/Classes/Service/DestinationDataImportService/DatesFactory.php
Daniel Siepmann (Codappix) 7b551547b6 Refactor date factory
Use a dedicated object to represent the incoming data.
That way the factory can ask the data/date questions and fetch data.
We do not need to expose the internals to the factory.
2025-02-11 14:28:39 +01:00

205 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace WerkraumMedia\Events\Service\DestinationDataImportService;
use DateInterval;
use DatePeriod;
use DateTimeImmutable;
use Generator;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Log\LogManager;
use WerkraumMedia\Events\Domain\DestinationData\Date as DestinationDataDate;
use WerkraumMedia\Events\Domain\Model\Date;
use WerkraumMedia\Events\Domain\Model\Import;
final class DatesFactory
{
private readonly LoggerInterface $logger;
public function __construct(
private readonly Context $context,
LogManager $logManager
) {
$this->logger = $logManager->getLogger(self::class);
}
/**
* @return Generator<Date>
*/
public function createDates(
Import $import,
array $timeIntervals,
bool $canceled
): Generator {
foreach ($timeIntervals as $date) {
$dates = $this->createDate($import, $date, $canceled);
if (!$dates instanceof Generator) {
return null;
}
foreach ($dates as $createdDate) {
yield $createdDate;
}
}
}
/**
* @return Generator<Date>|null
*/
private function createDate(
Import $import,
array $date,
bool $canceled
): ?Generator {
$date = new DestinationDataDate($date);
if ($date->isSingle()) {
$this->logger->info('Is single date', ['date' => $date]);
return $this->createSingleDate($date, $canceled);
}
if ($date->isInterval()) {
$this->logger->info('Is interval date', ['date' => $date]);
return $this->createDateFromInterval($import, $date, $canceled);
}
return null;
}
/**
* @return Generator<Date>
*/
private function createSingleDate(
DestinationDataDate $date,
bool $canceled
): Generator {
if ($date->isAfter($this->getToday())) {
yield Date::createFromDestinationDataDate($date, $canceled);
}
}
/**
* @return Generator<Date>|null
*/
private function createDateFromInterval(
Import $import,
DestinationDataDate $date,
bool $canceled
): ?Generator {
$date = $this->ensureRepeatUntil($import, $date);
if ($date->isDaily()) {
return $this->createDailyDates($date, $canceled);
}
if ($date->isWeekly()) {
return $this->createWeeklyDates($date, $canceled);
}
return null;
}
private function ensureRepeatUntil(
Import $import,
DestinationDataDate $date
): DestinationDataDate {
if ($date->hasKnownRepeat()) {
return $date;
}
$repeatUntil = $this->getToday()->modify($import->getRepeatUntil())->format('c');
$this->logger->info('Interval did not provide repeatUntil.', ['newRepeat' => $repeatUntil]);
return $date->withRepeatUntil($repeatUntil);
}
/**
* @return Generator<Date>
*/
private function createDailyDates(
DestinationDataDate $date,
bool $canceled
): Generator {
$today = $this->getToday();
$timeZone = $date->getTimeZone();
$start = $date->getStart();
$end = $date->getEnd();
$period = new DatePeriod($start, new DateInterval('P1D'), $date->getRepeatUntil());
foreach ($period as $day) {
$day = $day->setTimezone($timeZone);
if ($day < $today) {
$this->logger->debug('Date was in the past.', ['day' => $day]);
continue;
}
yield $this->createDateFromStartAndEnd(
$day,
$start,
$end,
$canceled
);
}
}
/**
* @return Generator<Date>
*/
private function createWeeklyDates(
DestinationDataDate $date,
bool $canceled
): Generator {
$today = $this->getToday();
$timeZone = $date->getTimeZone();
$start = $date->getStart();
$end = $date->getEnd();
$until = $date->getRepeatUntil();
foreach ($date->getWeekdays() as $day) {
$dateToUse = $start->modify($day);
$dateToUse = $dateToUse->setTime((int)$start->format('H'), (int)$start->format('i'));
$period = new DatePeriod($dateToUse, new DateInterval('P1W'), $until);
foreach ($period as $day) {
$day = $day->setTimezone($timeZone);
if ($day < $today) {
$this->logger->debug('Date was in the past.', ['day' => $day]);
continue;
}
yield $this->createDateFromStartAndEnd(
$day,
$start,
$end,
$canceled
);
}
}
}
private function createDateFromStartAndEnd(
DateTimeImmutable $dateToUse,
DateTimeImmutable $start,
DateTimeImmutable $end,
bool $canceled
): Date {
return Date::createFromDestinationData(
$dateToUse->setTime((int)$start->format('H'), (int)$start->format('i')),
$dateToUse->setTime((int)$end->format('H'), (int)$end->format('i')),
$canceled
);
}
private function getToday(): DateTimeImmutable
{
$today = $this->context->getPropertyFromAspect('date', 'full', new DateTimeImmutable());
if (!$today instanceof DateTimeImmutable) {
$today = new DateTimeImmutable();
}
return $today->modify('midnight');
}
}