Files
myep/src/Repository/TravelSnapshotRepository.php
T

130 lines
4.0 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\TravelSnapshot;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* Repository for querying and maintaining persisted travel snapshots.
*
* Provides focused lookup methods used by DB-primary travel loading,
* mapping generation, extended refresh scheduling, and retention cleanup.
*
* @extends ServiceEntityRepository<TravelSnapshot>
*/
class TravelSnapshotRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, TravelSnapshot::class);
}
/**
* Returns the exact snapshot for a travel date and hotel combination.
*/
public function findByDateAndHotel(int $dateId, int $hotelId): ?TravelSnapshot
{
return $this->findOneBy([
'dateId' => $dateId,
'hotelId' => $hotelId,
]);
}
/**
* Returns a fallback snapshot for the given date when hotel-specific lookup misses.
*/
public function findFirstByDateId(int $dateId): ?TravelSnapshot
{
return $this->createQueryBuilder('s')
->where('s.dateId = :dateId')
->setParameter('dateId', $dateId)
->orderBy('s.id', 'ASC')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
/**
* Returns the first known product ID for a travel date, if available.
*/
public function findProductIdByDateId(int $dateId): ?int
{
$value = $this->createQueryBuilder('s')
->select('s.productId')
->where('s.dateId = :dateId')
->andWhere('s.productId IS NOT NULL')
->setParameter('dateId', $dateId)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
if (null === $value) {
return null;
}
return (int) $value['productId'];
}
/**
* Returns all snapshots sorted deterministically for mapping aggregation.
*
* @return array<int, TravelSnapshot>
*/
public function findAllForMapping(?int $limit = null): array
{
$qb = $this->createQueryBuilder('s')
->orderBy('s.dateId', 'ASC')
->addOrderBy('s.hotelId', 'ASC');
if (null !== $limit) {
$qb->setMaxResults($limit);
}
return $qb->getQuery()->getResult();
}
/**
* Returns snapshots eligible for extended availability refresh.
*
* @return array<int, TravelSnapshot>
*/
public function findRefreshCandidates(
\DateTimeImmutable $dateToThreshold,
\DateTimeImmutable $refreshBefore,
int $limit,
): array {
return $this->createQueryBuilder('s')
// dateTo IS NULL: snapshots without a known end date are always considered active.
->where('s.dateTo IS NULL OR s.dateTo >= :dateToThreshold')
// extendedRefreshedAt IS NULL: never-refreshed snapshots rank as oldest.
->andWhere('s.extendedRefreshedAt IS NULL OR s.extendedRefreshedAt < :refreshBefore')
->setParameter('dateToThreshold', $dateToThreshold)
->setParameter('refreshBefore', $refreshBefore)
->orderBy('s.extendedRefreshedAt', 'ASC')
->addOrderBy('s.id', 'ASC')
->setMaxResults($limit)
->getQuery()
->getResult();
}
/**
* Deletes snapshots whose travel end date is older than the given threshold.
*/
public function deleteExpiredSnapshots(\DateTimeImmutable $beforeDate): int
{
return (int) $this->createQueryBuilder('s')
->delete()
// Snapshots with no known end date are excluded from purging as a safety net;
// they must be cleaned up manually if they become stale.
->where('s.dateTo IS NOT NULL')
->andWhere('s.dateTo < :beforeDate')
->setParameter('beforeDate', $beforeDate)
->getQuery()
->execute();
}
}