feat: persist travel snapshots and refresh extended availability
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<?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')
|
||||
->where('s.dateTo IS NULL OR s.dateTo >= :dateToThreshold')
|
||||
->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()
|
||||
->where('s.dateTo IS NOT NULL')
|
||||
->andWhere('s.dateTo < :beforeDate')
|
||||
->setParameter('beforeDate', $beforeDate)
|
||||
->getQuery()
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user