src/Controller/MapController.php line 67

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Location\City;
  4. use App\Entity\Profile\Genders;
  5. use App\Entity\Saloon\Saloon;
  6. use App\Entity\ServiceGroups;
  7. use App\Event\Profile\ProfilesShownEvent;
  8. use App\Form\FilterMapForm;
  9. use App\Repository\CityRepository;
  10. use App\Repository\ProfileRepository;
  11. use App\Repository\ReadModel\ProfileMapReadModel;
  12. use App\Repository\SaloonRepository;
  13. use App\Repository\ServiceRepository;
  14. use App\Service\Features;
  15. use App\Service\ProfileList;
  16. use App\Service\Map\MapFallbackService;
  17. use App\Service\Map\MapContextResolver;
  18. use App\Specification\Profile\ProfileHasMapCoordinates;
  19. use App\Specification\Profile\ProfileIsSuitableForTheMap;
  20. use App\Specification\Profile\ProfileIdINOrderedByINValues;
  21. use App\Specification\Profile\ProfileIsLocated;
  22. use App\Specification\QueryModifier\PossibleSaloonAdBoardPlacement;
  23. use App\Specification\QueryModifier\PossibleSaloonPlacementHiding;
  24. use App\Specification\Saloon\SaloonIsNotHidden;
  25. use App\Specification\QueryModifier\SaloonThumbnail;
  26. use App\Specification\Saloon\SaloonIsActive;
  27. use App\Specification\Saloon\SaloonIsSuitableForTheMap;
  28. use Happyr\DoctrineSpecification\Spec;
  29. use Psr\Cache\CacheItemPoolInterface;
  30. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  31. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  32. use Symfony\Component\Asset\Packages;
  33. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  34. use Symfony\Component\HttpFoundation\JsonResponse;
  35. use Symfony\Component\HttpFoundation\Request;
  36. use Symfony\Component\HttpFoundation\Response;
  37. use Symfony\Contracts\Cache\ItemInterface;
  38. use Symfony\Contracts\Translation\TranslatorInterface;
  39. class MapController extends AbstractController
  40. {
  41.     use ProfileMinPriceTrait;
  42.     const MAP_PROFILES_CACHE_ITEM_NAME = 'map_profiles_';
  43.     public function __construct(
  44.         private ProfileRepository        $profileRepository,
  45.         private CityRepository           $cityRepository,
  46.         private Features                 $features,
  47.         private Packages                 $assetPackage,
  48.         private SaloonRepository         $saloonRepository,
  49.         private ProfileList              $profileList,
  50.         private EventDispatcherInterface $eventDispatcher,
  51.         private ServiceRepository        $serviceRepository,
  52.         private CacheItemPoolInterface   $profilesFilterCache,
  53.         private MapFallbackService       $mapFallbackService,
  54.         private MapContextResolver       $mapContextResolver,
  55.     )
  56.     {
  57.     }
  58.     #[ParamConverter("city", converter: "city_converter")]
  59.     public function page(City $city, Request $request): Response
  60.     {
  61.         return $this->render('Map/page.html.twig', [
  62.             'cityUriIdentity' => $city->getUriIdentity(),
  63.             'cityLatitude' => $city->getMapCoordinate()->getLatitude(),
  64.             'cityLongitude' => $city->getMapCoordinate()->getLongitude(),
  65.             'multipleCities' => (int)$this->features->multiple_cities(),
  66.             'mapContext' => $this->mapContextResolver->isMainMap($request) ? 'main' : 'listing',
  67.         ]);
  68.     }
  69.     public function form(City $city, Request $request): Response
  70.     {
  71.         $form = $this->createForm(FilterMapForm::class, null, [
  72.             'data' => ['city_id' => $city->getId()],
  73.             'map_context' => $this->mapContextResolver->isMainMap($request) ? 'main' : 'listing',
  74.         ]);
  75.         return $this->render('Map/form.html.twig', [
  76.             'form' => $form->createView(),
  77.         ]);
  78.     }
  79.     public function filter(Request $request, TranslatorInterface $translator): Response
  80.     {
  81.         $params = json_decode($request->request->get('form'), true);
  82.         $form = $this->createForm(FilterMapForm::class);
  83.         $formParams = $params;
  84.         unset($formParams['scale']);
  85.         $form->submit($formParams);
  86.         $scale = $request->request->get('scale') ?? 0;
  87.         if ($scale <= 8) {
  88.             $coordsRoundPrecision = 1;
  89.         } else if ($scale <= 14) {
  90.             $coordsRoundPrecision = 2;
  91.         } else {
  92.             $coordsRoundPrecision = 4;
  93.         }
  94.         $city = $this->cityRepository->find($params['city_id']);
  95.         $profiles = $this->profileList->listForMap(
  96.             $city, null, $form->getData(), [
  97.             new ProfileHasMapCoordinates(),
  98.             new ProfileIsSuitableForTheMap(),
  99.         ], true, null,
  100.             ProfileList::ORDER_NONE, [Genders::FEMALE], $coordsRoundPrecision,
  101.         );
  102.         $specs = Spec::andX(
  103.             new SaloonIsSuitableForTheMap(),
  104.             new SaloonThumbnail(),
  105.             ProfileIsLocated::withinCity($city),
  106.             new ProfileHasMapCoordinates(),
  107.         );
  108.         $saloons = $this->saloonRepository->listForMapMatchingSpec($specs, $coordsRoundPrecision);
  109.         $rowConverter = function ($row) {
  110.             $row = array_values($row);
  111.             //id
  112.             $row[0] = array_map('intval', explode(',', $row[0]));
  113.             //coords
  114.             $row[3] = array_map('floatval', explode(',', $row[$row[2] == 1 ? 1 : 3]));
  115.             //is_masseur
  116.             if (isset($row[4])) {
  117.                 $row[4] = array_map('intval', explode(',', $row[4]));
  118.             }
  119.             array_splice($row, 1, 1);
  120.             return $row;
  121.         };
  122.         $out = [
  123.             'profiles' => array_map($rowConverter, $profiles),
  124.             'saloons' => array_map($rowConverter, $saloons),
  125.         ];
  126.         if ([] === $profiles && [] === $saloons && $form->isValid()
  127.             && $this->mapContextResolver->isMainMap($request, $params)
  128.         ) {
  129.             $out['profiles'] = $this->mapFallbackService->forLegacy($city, $coordsRoundPrecision);
  130.         }
  131.         return $this->json($out, headers: ['Cache-Control' => 'private, no-store']);
  132.     }
  133.     public function detail(Request $request, TranslatorInterface $translator): Response
  134.     {
  135.         $services = $this->serviceRepository->allIndexedById();
  136.         $profileIds = ($requestedProfiles = $request->request->get('profiles')) ? explode(',', $requestedProfiles) : [];
  137.         $saloonIds = ($requestedSaloons = $request->request->get('saloons')) ? explode(',', $requestedSaloons) : [];
  138.         $result = !empty($profileIds) ? $this->profileRepository->fetchMapProfilesByIds(new ProfileIdINOrderedByINValues($profileIds)) : [];
  139.         $profiles = [];
  140.         foreach ($result as /** @var ProfileMapReadModel $profile */ $profile) {
  141.             if (!$profile->mapLatitude || !$profile->mapLongitude)
  142.                 continue;
  143.             $path = $profile->avatar['path'];
  144.             $path = str_starts_with($path, '/') ? $path : substr($path, 6, -4);
  145.             $hasApartment = $profile->apartmentOneHourPrice || $profile->apartmentTwoHoursPrice || $profile->apartmentNightPrice;
  146.             $hasTakeout = $profile->takeOutOneHourPrice || $profile->takeOutTwoHoursPrice || $profile->takeOutNightPrice;
  147.             $tags = [];
  148.             if ($hasApartment && !$hasTakeout)
  149.                 $tags[] = 1;
  150.             elseif (!$hasApartment && $hasTakeout)
  151.                 $tags[] = 2;
  152.             elseif ($hasApartment && $hasTakeout)
  153.                 $tags[] = 3;
  154.             foreach ($profile->services as $serviceId) {
  155.                 $service = $services[$serviceId];
  156.                 switch ($service->getUriIdentity()) {
  157.                     case 'seks-klassicheskij':
  158.                     case 'sex':
  159.                         $tags[] = 4;
  160.                         break;
  161.                     case 'seks-analnyij':
  162.                     case 'anal-sex':
  163.                         $tags[] = 5;
  164.                         break;
  165.                     case 'minet-bez-rezinki':
  166.                     case 'blowjob-without-condom':
  167.                         $tags[] = 6;
  168.                         break;
  169.                     case 'kunnilingus':
  170.                     case 'cunnilingus-for-me':
  171.                         $tags[] = 7;
  172.                         break;
  173.                     case 'okonchanie-v-rot':
  174.                     case 'cum-in-mouth':
  175.                         $tags[] = 8;
  176.                         break;
  177.                 }
  178.                 if (ServiceGroups::MASSAGE === $service->getGroup() && false === in_array(9, $tags, true)) {
  179.                     $tags[] = 9;
  180.                 }
  181.             }
  182.             $profiles[] = [
  183.                 1,
  184.                 (float)rtrim(substr($profile->mapLatitude, 0, 7), '0'),
  185.                 (float)rtrim(substr($profile->mapLongitude, 0, 7), '0'),
  186.                 $profile->uriIdentity,
  187.                 $profile->name,
  188.                 $path, //$profile->avatar['path'],
  189.                 //$profile->avatar['type'] ? 'avatar' : 'photo',
  190.                 str_replace(' ', '', $profile->phoneNumber),
  191.                 $profile->station ?? 0,
  192.                 $profile->apartmentOneHourPrice ?? $profile->takeOutOneHourPrice ?? 0,
  193.                 $profile->apartmentTwoHoursPrice ?? $profile->takeOutTwoHoursPrice ?? 0,
  194.                 $profile->apartmentNightPrice ?? $profile->takeOutNightPrice ?? 0,
  195.                 (int)$profile->isApproved,
  196.                 (int)$profile->isMasseur,
  197.                 (int)$profile->hasComments,
  198.                 (int)$profile->hasSelfies,
  199.                 (int)$profile->hasVideos,
  200.                 $profile->age ?? 0,
  201.                 $profile->breastSize ?? 0,
  202.                 $profile->height ?? 0,
  203.                 $profile->weight ?? 0,
  204.                 $tags,
  205.                 $profile->id,
  206.                 (int)$profile->isPaid ?? 0,
  207.             ];
  208.         }
  209.         $result = !empty($saloonIds) ? $this->saloonRepository->matchingSpecRaw(new ProfileIdINOrderedByINValues($saloonIds), null, false) : [];
  210.         $saloons = [];
  211.         foreach ($result as /** @var Saloon $saloon */ $saloon) {
  212.             $photoPath = null !== ($mainPhoto = $saloon->getThumbnail()) ? $mainPhoto->getPath() : '';
  213.             $photoPath = str_starts_with($photoPath, '/') ? $photoPath : str_replace(".jpg", "", substr($photoPath, 6));
  214.             $saloons[] = [
  215.                 2,
  216.                 (float)rtrim(substr($saloon->getMapCoordinate()->getLatitude(), 0, 7), '0'),
  217.                 (float)rtrim(substr($saloon->getMapCoordinate()->getLongitude(), 0, 7), '0'),
  218.                 $saloon->getUriIdentity(),
  219.                 $translator->trans($saloon->getName()),
  220.                 $photoPath,
  221.                 //'thumb',
  222.                 str_replace(' ', '', $saloon->getPhoneNumber()),
  223.                 $saloon->getPrimaryStation()?->getId() ?? 0,
  224.                 $saloon->getApartmentsPricing()->getOneHourPrice() ?? $saloon->getTakeOutPricing()->getOneHourPrice() ?? 0,
  225.                 $saloon->getApartmentsPricing()->getTwoHoursPrice() ?? $saloon->getTakeOutPricing()->getTwoHoursPrice() ?? 0,
  226.                 $saloon->getApartmentsPricing()->getNightPrice() ?? $saloon->getTakeOutPricing()->getNightPrice() ?? 0,
  227.                 //$saloon->getExpressPricing()->isProvided() ? $saloon->getExpressPricing()->getPrice() ?? 0 : 0,
  228.                 (int)($saloon?->getAdBoardPlacement() !== null),
  229.                 $saloon->getId(),
  230.             ];
  231.         }
  232.         return new JsonResponse([
  233.             'profiles' => $profiles,
  234.             'saloons' => $saloons,
  235.         ]);
  236.     }
  237.     public function processProfileShows(Request $request, ProfileRepository $profileRepository): JsonResponse
  238.     {
  239.         $id = $request->query->get('id');
  240.         $profile = $profileRepository->find($id);
  241.         if ($profile) {
  242.             $this->eventDispatcher->dispatch(new ProfilesShownEvent([$profile->getId()], 'map'), ProfilesShownEvent::NAME);
  243.         }
  244.         return $this->json([]);
  245.     }
  246.     public function cachedMapProfilesResultByIds(ProfileIdINOrderedByINValues $specification)
  247.     {
  248.         $key = sha1(self::MAP_PROFILES_CACHE_ITEM_NAME . implode(',', $specification->getIds()));
  249.         return $this->profilesFilterCache->get($key, function (ItemInterface $item) use ($specification) {
  250.             return $this->profileRepository->fetchMapProfilesByIds($specification);
  251.         });
  252.     }
  253. }