src/Service/HomepageCityListingsBlockProvider.php line 42

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Dto\HomepageCityListingsBlockData;
  4. use App\Entity\Location\City;
  5. use App\Repository\SaloonRepository;
  6. use App\Specification\QueryModifier\FreeProfilesFeatureSaloonOrder;
  7. use App\Specification\QueryModifier\SaloonOrderedByStatus;
  8. use App\Specification\Saloon\SaloonIsActive;
  9. use App\Specification\Saloon\SaloonIsLocated;
  10. use App\Specification\Saloon\SaloonIsNotHidden;
  11. use Happyr\DoctrineSpecification\Spec;
  12. use Psr\Cache\CacheItemPoolInterface;
  13. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  14. class HomepageCityListingsBlockProvider
  15. {
  16.     private const CACHE_KEY_MASSEURS_FALLBACK_ITEMS 'homepage_city_masseurs_fallback_items';
  17.     private const CACHE_KEY_SALOONS_IDS 'homepage_city_saloons_ids';
  18.     public function __construct(
  19.         private ListingRotationApi $listingRotationApi,
  20.         private ProfileList $profileList,
  21.         private ProfileListSpecificationService $profileListSpecificationService,
  22.         private SaloonRepository $saloonRepository,
  23.         private Features $features,
  24.         private ParameterBagInterface $parameterBag,
  25.         private CacheItemPoolInterface $projectDataCache,
  26.     ) {}
  27.     public function getForCity(City $city): HomepageCityListingsBlockData
  28.     {
  29.         $masseurs = [];
  30.         $saloons = [];
  31.         if ($this->features->has_masseurs() && $this->features->homepage_city_masseurs_block()) {
  32.             $masseurs $this->getMasseursFromListing($city$this->getMasseursCount());
  33.         }
  34.         if ($this->features->has_saloons() && $this->features->homepage_city_saloons_block()) {
  35.             $saloons $this->getSaloonsFromListing($city$this->getSaloonsCount());
  36.         }
  37.         return new HomepageCityListingsBlockData($masseurs$saloons);
  38.     }
  39.     private function getMasseursFromListing(City $cityint $count): array
  40.     {
  41.         if ($count 1) {
  42.             return [];
  43.         }
  44.         try {
  45.             return $this->listingRotationApi->listLimited('/city/{city}/masseur', ['city' => $city->getId()], $count);
  46.         } catch (\Throwable) {
  47.             try {
  48.                 $specs $this->profileListSpecificationService->listForMasseur($city);
  49.                 $cacheKey sprintf('%s_%s_%s'self::CACHE_KEY_MASSEURS_FALLBACK_ITEMS$city->getId(), $count);
  50.                 return $this->rememberItems($cacheKey, function() use ($city$specs$count): array {
  51.                     $result $this->profileList->listActiveWithinCityOrderedByStatusWithSpecLimited(
  52.                         $city,
  53.                         $specs->spec(),
  54.                         $specs->additionalSpecs(),
  55.                         $specs->genders(),
  56.                         true,
  57.                         $count,
  58.                     );
  59.                     return is_array($result) ? $result : [];
  60.                 });
  61.             } catch (\Throwable) {
  62.                 return [];
  63.             }
  64.         }
  65.     }
  66.     private function getSaloonsFromListing(City $cityint $count): array
  67.     {
  68.         if ($count 1) {
  69.             return [];
  70.         }
  71.         $cacheKey sprintf(
  72.             '%s_%s_%s_%s',
  73.             self::CACHE_KEY_SALOONS_IDS,
  74.             $city->getId(),
  75.             $count,
  76.             (int) $this->features->free_profiles(),
  77.         );
  78.         $ids $this->rememberIds($cacheKey, function() use ($city$count): array {
  79.             $criteria Spec::andX(
  80.                 $this->features->free_profiles() ? new SaloonIsNotHidden() : new SaloonIsActive(),
  81.                 SaloonIsLocated::withinCity($city),
  82.                 $this->features->free_profiles() ? new FreeProfilesFeatureSaloonOrder() : new SaloonOrderedByStatus()
  83.             );
  84.             $result $this->saloonRepository->matchingSpec($criteria)->take(0$count);
  85.             return $this->extractIds($result$count);
  86.         });
  87.         if (empty($ids)) {
  88.             return [];
  89.         }
  90.         $saloons $this->saloonRepository->findBy(['id' => $ids]);
  91.         return $this->sortByIds($saloons$ids);
  92.     }
  93.     private function rememberIds(string $key, callable $resolver): array
  94.     {
  95.         $cacheItem $this->projectDataCache->getItem($key);
  96.         if ($cacheItem->isHit()) {
  97.             $value $cacheItem->get();
  98.             return is_array($value) ? $value : [];
  99.         }
  100.         $value $resolver();
  101.         $ids array_values(array_unique(array_filter($value, static function($id): bool {
  102.             return is_int($id) || ctype_digit((string) $id);
  103.         })));
  104.         $cacheItem->set($ids);
  105.         $cacheItem->expiresAfter(60);
  106.         $this->projectDataCache->save($cacheItem);
  107.         return $ids;
  108.     }
  109.     private function rememberItems(string $key, callable $resolver): array
  110.     {
  111.         $cacheItem $this->projectDataCache->getItem($key);
  112.         if ($cacheItem->isHit()) {
  113.             $value $cacheItem->get();
  114.             return is_array($value) ? $value : [];
  115.         }
  116.         $value $resolver();
  117.         $items is_array($value) ? $value : [];
  118.         $cacheItem->set($items);
  119.         $cacheItem->expiresAfter(60);
  120.         $this->projectDataCache->save($cacheItem);
  121.         return $items;
  122.     }
  123.     private function extractIds(iterable $itemsint $limit): array
  124.     {
  125.         $ids = [];
  126.         foreach ($items as $item) {
  127.             $id $this->extractId($item);
  128.             if (null === $id) {
  129.                 continue;
  130.             }
  131.             $ids[] = $id;
  132.             if (count($ids) >= $limit) {
  133.                 break;
  134.             }
  135.         }
  136.         return $ids;
  137.     }
  138.     private function extractId(mixed $item): ?int
  139.     {
  140.         if (is_object($item) && method_exists($item'getId')) {
  141.             return (int) $item->getId();
  142.         }
  143.         if (is_object($item) && isset($item->id)) {
  144.             return (int) $item->id;
  145.         }
  146.         if (is_array($item) && isset($item['id'])) {
  147.             return (int) $item['id'];
  148.         }
  149.         return null;
  150.     }
  151.     private function sortByIds(array $items, array $ids): array
  152.     {
  153.         $itemsById = [];
  154.         foreach ($items as $item) {
  155.             $id $this->extractId($item);
  156.             if (null !== $id) {
  157.                 $itemsById[$id] = $item;
  158.             }
  159.         }
  160.         $sorted = [];
  161.         foreach ($ids as $id) {
  162.             if (isset($itemsById[$id])) {
  163.                 $sorted[] = $itemsById[$id];
  164.             }
  165.         }
  166.         return $sorted;
  167.     }
  168.     private function getMasseursCount(): int
  169.     {
  170.         return max(0, (int) $this->parameterBag->get('homepage.city.masseurs_block.count'));
  171.     }
  172.     private function getSaloonsCount(): int
  173.     {
  174.         return max(0, (int) $this->parameterBag->get('homepage.city.saloons_block.count'));
  175.     }
  176. }