<?php
namespace App\Service;
use App\Dto\Top100\Top100ProfileQueryByIdsDTO;
use App\Dto\Top100\Top100ProfileQueryRandomDTO;
use App\Entity\Location\City;
use App\Entity\Profile\Genders;
use App\Repository\ProfileCtrRepository;
use App\Service\Top100\Top100ProfileQueryService;
use App\Specification\Profile\ProfileIdNotIn;
use App\Specification\Profile\ProfileIsApproved;
use Psr\Cache\CacheItemPoolInterface;
class Top100ProfilesService
{
public const LIMIT = 100;
public const GENDERS_DEFAULT = [Genders::FEMALE];
public function __construct(
private ProfileCtrRepository $profileCtrRepository,
private Top100ProfileQueryService $profileQueryService,
private CacheItemPoolInterface $cache,
) {}
public function getCachedProfileIds(City $city): array
{
$cacheKey = $this->getCacheKey($city);
$cacheItem = $this->cache->getItem($cacheKey);
if (!$cacheItem->isHit()) {
return [];
}
$profileIds = $cacheItem->get();
return is_array($profileIds) ? $profileIds : [];
}
public function refreshProfileIdsCache(City $city): array
{
$cacheKey = $this->getCacheKey($city);
$profileIds = $this->buildProfileIds($city);
$cacheItem = $this->cache->getItem($cacheKey);
$cacheItem->set($profileIds);
$this->cache->save($cacheItem);
return $profileIds;
}
private function buildProfileIds(City $city): array
{
$dateStart = new \DateTimeImmutable('-30 days');
$rankedIds = $this->profileCtrRepository->topVisitedApprovedProfileIdsInCity($city, $dateStart, self::LIMIT);
$rankedProfiles = empty($rankedIds)
? []
: $this->profileQueryService->getActiveProfilesByIds(
new Top100ProfileQueryByIdsDTO(
city: $city,
ids: $rankedIds,
filters: [new ProfileIsApproved()],
genders: self::GENDERS_DEFAULT,
limit: self::LIMIT,
)
);
$ranked = array_map(static fn($profile) => $profile->id, $rankedProfiles);
if (count($ranked) >= self::LIMIT) {
return array_slice($ranked, 0, self::LIMIT);
}
$toFill = self::LIMIT - count($ranked);
$randomFilters = [new ProfileIsApproved()];
if (!empty($ranked)) {
$randomFilters[] = new ProfileIdNotIn($ranked);
}
$randomDto = new Top100ProfileQueryRandomDTO(
city: $city,
filters: $randomFilters,
genders: self::GENDERS_DEFAULT,
limit: $toFill,
);
$randomApproved = $this->profileQueryService->getRandomProfiles($randomDto);
$randomIds = array_map(static fn($profile) => $profile->id, $randomApproved);
return array_merge($ranked, $randomIds);
}
private function getCacheKey(City $city): string
{
return sprintf('listing.top_100.profile_ids.city.%d', $city->getId());
}
public function getSortedProfilesByVisits(City $city): \Porpaginas\Page
{
$rankedIds = $this->getCachedProfileIds($city);
$rankedProfiles = $this->profileQueryService->getActiveProfilesByIds(
new Top100ProfileQueryByIdsDTO(
city: $city,
ids: $rankedIds,
filters: [new ProfileIsApproved()],
genders: self::GENDERS_DEFAULT,
limit: self::LIMIT,
)
);
return $this->profileQueryService->createFakePageFromProfiles(count($rankedProfiles), $rankedProfiles);
}
}