src/Service/GeoCategoryLinkageService.php line 205

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Dto\ProfileListSpecification;
  4. use App\Entity\Location\County;
  5. use App\Entity\Location\District;
  6. use App\Entity\Location\Station;
  7. use App\Entity\Profile\Genders;
  8. use Psr\Cache\CacheItemPoolInterface;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\HttpFoundation\RequestStack;
  11. use Symfony\Component\Routing\RouterInterface;
  12. use Symfony\Contracts\Cache\ItemInterface;
  13. class GeoCategoryLinkageService
  14. {
  15.     private const CACHE_ITEM_PREFIX 'geo_category_linkage_count';
  16.     private const CACHE_TTL 300;
  17.     private const CATEGORIES = [
  18.         'eromassage' => [
  19.             'text' => 'Эромассаж',
  20.             'feature' => 'extra_category_eromassage',
  21.         ],
  22.         'verified' => [
  23.             'text' => 'Проверенные',
  24.             'feature' => 'extra_category_verified',
  25.         ],
  26.         'cheap' => [
  27.             'text' => 'Дешевые',
  28.             'feature' => 'extra_category_cheap',
  29.         ],
  30.         'mature' => [
  31.             'text' => 'Зрелые',
  32.             'feature' => 'extra_category_mature',
  33.         ],
  34.         'uzbek' => [
  35.             'text' => 'Узбечки',
  36.             'feature' => 'extra_category_uzbek',
  37.         ],
  38.     ];
  39.     private const GEO_ENTITY_CLASSES = [
  40.         'county' => County::class,
  41.         'district' => District::class,
  42.         'station' => Station::class,
  43.     ];
  44.     private ?Request $request;
  45.     public function __construct(
  46.         private Features $features,
  47.         private ProfileList $profileList,
  48.         private ProfileListSpecificationService $profileListSpecificationService,
  49.         private RouterInterface $router,
  50.         private CacheItemPoolInterface $projectDataCache,
  51.         RequestStack $requestStack
  52.     ) {
  53.         $this->request $requestStack->getCurrentRequest();
  54.     }
  55.     public function links(): ?array
  56.     {
  57.         if (null === $this->request) {
  58.             return null;
  59.         }
  60.         $context $this->resolveContext($this->normalizeRoute($this->request->attributes->get('_route')));
  61.         if (null === $context) {
  62.             return null;
  63.         }
  64.         $links = [];
  65.         if (null !== $context['current_category']) {
  66.             $profilesCount $this->countProfiles($context['geo_type'], $context['geo']);
  67.             if ($profilesCount 0) {
  68.                 $links[] = $this->createLink(
  69.                     $context['main_route'],
  70.                     $context['route_parameters'],
  71.                     'Проститутки',
  72.                     $profilesCount
  73.                 );
  74.             }
  75.         }
  76.         foreach (self::CATEGORIES as $category => $configuration) {
  77.             if ($category === $context['current_category'] || !$this->featureEnabled($configuration['feature'])) {
  78.                 continue;
  79.             }
  80.             $profilesCount $this->countProfiles(
  81.                 $context['geo_type'],
  82.                 $context['geo'],
  83.                 $category
  84.             );
  85.             if ($profilesCount 1) {
  86.                 continue;
  87.             }
  88.             $links[] = $this->createLink(
  89.                 $context['main_route'] . '_' $category,
  90.                 $context['route_parameters'],
  91.                 $configuration['text'],
  92.                 $profilesCount,
  93.                 null === $context['current_category']
  94.             );
  95.         }
  96.         return $links ?: null;
  97.     }
  98.     private function resolveContext(string $route): ?array
  99.     {
  100.         foreach (self::GEO_ENTITY_CLASSES as $geoType => $entityClass) {
  101.             $mainRoute 'profile_list.list_by_' $geoType;
  102.             $currentCategory null;
  103.             if ($route !== $mainRoute) {
  104.                 foreach (array_keys(self::CATEGORIES) as $category) {
  105.                     if ($route === $mainRoute '_' $category) {
  106.                         $currentCategory $category;
  107.                         break;
  108.                     }
  109.                 }
  110.                 if (null === $currentCategory) {
  111.                     continue;
  112.                 }
  113.             }
  114.             $geo $this->request->attributes->get($geoType);
  115.             if (!$geo instanceof $entityClass) {
  116.                 return null;
  117.             }
  118.             return [
  119.                 'geo_type' => $geoType,
  120.                 'geo' => $geo,
  121.                 'main_route' => $mainRoute,
  122.                 'current_category' => $currentCategory,
  123.                 'route_parameters' => [
  124.                     'city' => $geo->getCity()->getUriIdentity(),
  125.                     $geoType => $geo->getUriIdentity(),
  126.                 ],
  127.             ];
  128.         }
  129.         return null;
  130.     }
  131.     private function countProfiles(
  132.         string $geoType,
  133.         County|District|Station $geo,
  134.         string $category ''
  135.     ): int {
  136.         $cacheKey sprintf(
  137.             '%s.%d.%s.%d.%s.%d.%d',
  138.             self::CACHE_ITEM_PREFIX,
  139.             $geo->getCity()->getId(),
  140.             $geoType,
  141.             $geo->getId(),
  142.             $category ?: 'base',
  143.             (int) $this->features->hard_moderation(),
  144.             (int) $this->features->free_profiles()
  145.         );
  146.         return $this->projectDataCache->get($cacheKey, function (ItemInterface $item) use ($geoType$geo$category): int {
  147.             $item->expiresAfter(self::CACHE_TTL);
  148.             $specification $this->createSpecification($geoType$geo$category);
  149.             return $this->profileList->countActiveWithinCityWithSpec(
  150.                 $geo->getCity(),
  151.                 $specification->spec(),
  152.                 $specification->additionalSpecs(),
  153.                 $specification->genders() ?? [Genders::FEMALE]
  154.             );
  155.         });
  156.     }
  157.     private function createSpecification(
  158.         string $geoType,
  159.         County|District|Station $geo,
  160.         string $category
  161.     ): ProfileListSpecification {
  162.         switch ($geoType) {
  163.             case 'county':
  164.                 if (!$geo instanceof County) {
  165.                     break;
  166.                 }
  167.                 switch ($category) {
  168.                     case '':
  169.                         return $this->profileListSpecificationService->listByCounty($geo);
  170.                     case 'eromassage':
  171.                         return $this->profileListSpecificationService->listByCountyEromassage($geo);
  172.                     case 'verified':
  173.                         return $this->profileListSpecificationService->listByCountyVerified($geo);
  174.                     case 'cheap':
  175.                         return $this->profileListSpecificationService->listByCountyCheap($geo);
  176.                     case 'mature':
  177.                         return $this->profileListSpecificationService->listByCountyMature($geo);
  178.                     case 'uzbek':
  179.                         return $this->profileListSpecificationService->listByCountyUzbek($geo);
  180.                 }
  181.                 break;
  182.             case 'district':
  183.                 if (!$geo instanceof District) {
  184.                     break;
  185.                 }
  186.                 switch ($category) {
  187.                     case '':
  188.                         return $this->profileListSpecificationService->listByDistrict($geo);
  189.                     case 'eromassage':
  190.                         return $this->profileListSpecificationService->listByDistrictEromassage($geo);
  191.                     case 'verified':
  192.                         return $this->profileListSpecificationService->listByDistrictVerified($geo);
  193.                     case 'cheap':
  194.                         return $this->profileListSpecificationService->listByDistrictCheap($geo);
  195.                     case 'mature':
  196.                         return $this->profileListSpecificationService->listByDistrictMature($geo);
  197.                     case 'uzbek':
  198.                         return $this->profileListSpecificationService->listByDistrictUzbek($geo);
  199.                 }
  200.                 break;
  201.             case 'station':
  202.                 if (!$geo instanceof Station) {
  203.                     break;
  204.                 }
  205.                 switch ($category) {
  206.                     case '':
  207.                         return $this->profileListSpecificationService->listByStation($geo);
  208.                     case 'eromassage':
  209.                         return $this->profileListSpecificationService->listByStationEromassage($geo);
  210.                     case 'verified':
  211.                         return $this->profileListSpecificationService->listByStationVerified($geo);
  212.                     case 'cheap':
  213.                         return $this->profileListSpecificationService->listByStationCheap($geo);
  214.                     case 'mature':
  215.                         return $this->profileListSpecificationService->listByStationMature($geo);
  216.                     case 'uzbek':
  217.                         return $this->profileListSpecificationService->listByStationUzbek($geo);
  218.                 }
  219.                 break;
  220.         }
  221.         throw new \LogicException(sprintf('Unsupported geo category "%s" for "%s".'$category$geoType));
  222.     }
  223.     private function createLink(
  224.         string $route,
  225.         array $routeParameters,
  226.         string $text,
  227.         int $profilesCount,
  228.         bool $additive false
  229.     ): array {
  230.         return [
  231.             'href' => $this->router->generate($route$routeParameters),
  232.             'text' => $text,
  233.             'profiles_count' => $profilesCount,
  234.             'additive' => $additive,
  235.         ];
  236.     }
  237.     private function featureEnabled(string $feature): bool
  238.     {
  239.         switch ($feature) {
  240.             case 'extra_category_eromassage':
  241.                 return $this->features->extra_category_eromassage();
  242.             case 'extra_category_verified':
  243.                 return $this->features->extra_category_verified();
  244.             case 'extra_category_cheap':
  245.                 return $this->features->extra_category_cheap();
  246.             case 'extra_category_mature':
  247.                 return $this->features->extra_category_mature();
  248.             case 'extra_category_uzbek':
  249.                 return $this->features->extra_category_uzbek();
  250.         }
  251.         throw new \LogicException(sprintf('Unsupported geo category feature "%s".'$feature));
  252.     }
  253.     private function normalizeRoute(?string $route): string
  254.     {
  255.         $route preg_replace('/\.(?:ru|en)$/''', (string) $route);
  256.         return preg_replace('/\._pagination$/'''$route);
  257.     }
  258. }