src/Controller/ProfileListController.php line 2389

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-03-19
  5.  * Time: 22:28
  6.  */
  7. namespace App\Controller;
  8. use App\Bridge\Porpaginas\Doctrine\ORM\FakeORMQueryPage;
  9. use App\Entity\Location\City;
  10. use App\Entity\Location\County;
  11. use App\Entity\Location\District;
  12. use App\Entity\Location\Station;
  13. use App\Entity\Profile\BodyTypes;
  14. use App\Entity\Profile\BreastTypes;
  15. use App\Entity\Profile\Genders;
  16. use App\Entity\Profile\HairColors;
  17. use App\Entity\Profile\Nationalities;
  18. use App\Entity\Profile\PrivateHaircuts;
  19. use App\Entity\Service;
  20. use App\Entity\ServiceGroups;
  21. use App\Entity\TakeOutLocations;
  22. use App\Repository\ServiceRepository;
  23. use App\Repository\StationRepository;
  24. use App\Service\CountryCurrencyResolver;
  25. use App\Service\DefaultCityProvider;
  26. use App\Service\Features;
  27. use App\Service\HomepageCityListingsBlockProvider;
  28. use App\Service\ListingRotationApi;
  29. use App\Service\ListingService;
  30. use App\Service\ProfileList;
  31. use App\Service\ProfileListingDataCreator;
  32. use App\Service\ProfileListSpecificationService;
  33. use App\Service\ProfileFilterService;
  34. use App\Service\ProfileTopBoard;
  35. use App\Service\Top100ProfilesService;
  36. use App\Specification\ElasticSearch\ISpecification;
  37. use App\Specification\Profile\ProfileHasApartments;
  38. use App\Specification\Profile\ProfileHasComments;
  39. use App\Specification\Profile\ProfileHasVideo;
  40. use App\Specification\Profile\ProfileIdIn;
  41. use App\Specification\Profile\ProfileIdINOrderedByINValues;
  42. use App\Specification\Profile\ProfileIdNotIn;
  43. use App\Specification\Profile\ProfileIsApproved;
  44. use App\Specification\Profile\ProfileIsElite;
  45. use App\Specification\Profile\ProfileIsLocated;
  46. use App\Specification\Profile\ProfileIsProvidingOneOfServices;
  47. use App\Specification\Profile\ProfileIsProvidingTakeOut;
  48. use App\Specification\Profile\ProfileWithAge;
  49. use App\Specification\Profile\ProfileWithBodyType;
  50. use App\Specification\Profile\ProfileWithBreastType;
  51. use App\Specification\Profile\ProfileWithHairColor;
  52. use App\Specification\Profile\ProfileWithNationality;
  53. use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
  54. use App\Specification\Profile\ProfileWithPrivateHaircut;
  55. use Flagception\Bundle\FlagceptionBundle\Annotations\Feature;
  56. use Happyr\DoctrineSpecification\Filter\Filter;
  57. use Happyr\DoctrineSpecification\Logic\OrX;
  58. use Porpaginas\Doctrine\ORM\ORMQueryResult;
  59. use Porpaginas\Page;
  60. use Psr\Cache\CacheItemPoolInterface;
  61. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  62. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
  63. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  64. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  65. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  66. use Symfony\Component\HttpFoundation\Request;
  67. use Happyr\DoctrineSpecification\Spec;
  68. use Symfony\Component\HttpFoundation\RequestStack;
  69. use Symfony\Component\HttpFoundation\Response;
  70. /**
  71.  * @see \App\Console\Export\ExportRotationListingsConfigCommand for listing API endpoints
  72.  */
  73. #[Cache(maxage60, public: true)]
  74. class ProfileListController extends AbstractController
  75. {
  76.     use ExtendedPaginationTrait;
  77.     use SpecTrait;
  78.     use ProfileMinPriceTrait;
  79.     use ResponseTrait;
  80.     const ENTRIES_ON_PAGE 36;
  81.     const RESULT_SOURCE_COUNTY 'county';
  82.     const RESULT_SOURCE_DISTRICT 'district';
  83.     const RESULT_SOURCE_STATION 'station';
  84.     const RESULT_SOURCE_APPROVED 'approved';
  85.     const RESULT_SOURCE_WITH_COMMENTS 'with_comments';
  86.     const RESULT_SOURCE_WITH_VIDEO 'with_video';
  87.     const RESULT_SOURCE_WITH_SELFIE 'with_selfie';
  88.     const RESULT_SOURCE_TOP_100 'top_100';
  89.     const RESULT_SOURCE_ELITE 'elite';
  90.     const RESULT_SOURCE_MASSEURS 'masseurs';
  91.     const RESULT_SOURCE_MASSAGE_SERVICE 'massage_service';
  92.     const RESULT_SOURCE_BY_PARAMS 'by_params';
  93.     const RESULT_SOURCE_SERVICE 'service';
  94.     const RESULT_SOURCE_CITY 'city';
  95.     const RESULT_SOURCE_COUNTRY 'country';
  96.     const CACHE_ITEM_STATION_ADDED_PROFILES 'station_added_profiles_ids_';
  97.     const RESULT_SOURCE_WITH_WHATSAPP 'with_whatsapp';
  98.     const RESULT_SOURCE_WITH_TELEGRAM 'with_telegram';
  99.     const RESULT_SOURCE_EIGHTEEN_YEARS_OLD 'eighteen_years_old';
  100.     const RESULT_SOURCE_BIG_ASS 'big_ass';
  101.     const RESULT_SOURCE_WITH_TATTOO 'with_tattoo';
  102.     const RESULT_SOURCE_WITH_PIERCING 'with_piercing';
  103.     const RESULT_SOURCE_ROUND_THE_CLOCK 'round_the_clock';
  104.     const RESULT_SOURCE_FOR_TWO_HOURS 'for_two_hours';
  105.     const RESULT_SOURCE_FOR_HOUR 'for_hour';
  106.     const RESULT_SOURCE_EXPRESS_PROGRAM 'express_program';
  107.     const RESULT_SOURCE_EROMASSAGE 'eromassage';
  108.     const RESULT_SOURCE_VERIFIED 'verified';
  109.     const RESULT_SOURCE_CHEAP 'cheap';
  110.     const RESULT_SOURCE_MATURE 'mature';
  111.     const RESULT_SOURCE_UZBEK 'uzbek';
  112.     private ?string $source null;
  113.     public function __construct(
  114.         private RequestStack $requestStack,
  115.         private ProfileList                     $profileList,
  116.         private CountryCurrencyResolver         $countryCurrencyResolver,
  117.         private ServiceRepository               $serviceRepository,
  118.         private ListingService                  $listingService,
  119.         private Features                        $features,
  120.         private ProfileFilterService            $profilesFilterService,
  121.         private ProfileListSpecificationService $profileListSpecificationService,
  122.         private ProfileListingDataCreator       $profileListingDataCreator,
  123.         private Top100ProfilesService           $top100ProfilesService,
  124.         private CacheItemPoolInterface          $stationAddedProfilesCache,
  125.         private ParameterBagInterface           $parameterBag,
  126.         private ListingRotationApi              $listingRotationApi,
  127.         private ProfileTopBoard                 $profileTopBoard,
  128.         private HomepageCityListingsBlockProvider $homepageCityListingsBlockProvider,
  129.     ) {}
  130.     /**
  131.      * @Feature("has_masseurs")
  132.      */
  133.     #[ParamConverter("city"converter"city_converter")]
  134.     public function listForMasseur(City $cityServiceRepository $serviceRepository): Response
  135.     {
  136.         $specs $this->profileListSpecificationService->listForMasseur($city);
  137.         $response = new Response();
  138.         $massageGroupServices $serviceRepository->findBy(['group' => ServiceGroups::MASSAGE]);
  139.         $alternativeSpec $this->getORSpecForItemsArray([$massageGroupServices], function($item): ProfileIsProvidingOneOfServices {
  140.             return new ProfileIsProvidingOneOfServices($item);
  141.         });
  142.         $result $this->paginatedListing($city'/city/{city}/masseur', ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_MASSAGE_SERVICE$response);
  143.         return $this->render('ProfileList/list.html.twig', [
  144.             'profiles' => $result,
  145.             'source' => $this->source,
  146.             'source_default' => self::RESULT_SOURCE_MASSEURS,
  147.             'recommendationSpec' => $specs->recommendationSpec(),
  148.         ], response$response);
  149.     }
  150.     /**
  151.      * @Feature("extra_category_big_ass")
  152.      */
  153.     #[ParamConverter("city"converter"city_converter")]
  154.     public function listBigAss(Request $requestCity $city): Response
  155.     {
  156.         $specs $this->profileListSpecificationService->listBigAss();
  157.         $response = new Response();
  158.         $result $this->paginatedListing($city'/city/{city}/category/big_ass', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  159.         return $this->render('ProfileList/list.html.twig', [
  160.             'profiles' => $result,
  161.             'source' => $this->source,
  162.             'source_default' => self::RESULT_SOURCE_BIG_ASS,
  163.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  164.                 'city' => $city->getUriIdentity(),
  165.                 'page' => $this->getCurrentPageNumber(),
  166.             ]),
  167.             'recommendationSpec' => $specs->recommendationSpec(),
  168.         ], response$response);
  169.     }
  170.     public function listByDefaultCity(ParameterBagInterface $parameterBagRequest $request): Response
  171.     {
  172.         $controller get_class($this).'::listByCity';
  173.         $path = [
  174.             'city' => $parameterBag->get('default_city'),
  175.             'subRequest' => true,
  176.         ];
  177.         //чтобы в обработчике можно было понять, по какому роуту зашли
  178.         $request->request->set('_route''profile_list.list_by_city');
  179.         return $this->forward($controller$path);
  180.     }
  181.     private function paginatedListing(City $city, ?string $apiEndpoint, array $apiParams, ?Filter $listingSpec null, ?OrX $alternativeSpec null, ?string $alternativeSource null, ?Response $response null): Page
  182.     {
  183.         $topPlacement $this->profileTopBoard->topPlacementSatisfiedBy($city$listingSpec);
  184.         $topPlacement?->setTopCard(); // mark as top card for UI
  185.         $page $this->getCurrentPageNumber();
  186.         $apiParams['city'] = $city->getId();
  187.         try {
  188.             if (null === $apiEndpoint) {
  189.                 throw new \RuntimeException('Empty API endpoint to switch to legacy listing query.');
  190.             }
  191.             $result $this->listingRotationApi->paginate($apiEndpoint$apiParams$page$topPlacement);
  192.             $response?->setMaxAge(10);
  193.         } catch (\Exception) {
  194.             $avoidOrTopPlacement = (null !== $topPlacement && $page 2) ? $topPlacement null;
  195.             $result $this->profileList->list($citynull$listingSpec, [], truenullProfileList::ORDER_BY_STATUS,
  196.                 nulltruenull, [Genders::FEMALE], $avoidOrTopPlacement);
  197.         }
  198.         if (null !== $alternativeSpec || null !== $alternativeSource) {
  199.             $prevCount $result->count();
  200.             $result $this->checkEmptyResultNotMasseur($result$city$alternativeSpec$alternativeSource);
  201.             if ($result->count() > $prevCount) {
  202.                 $response?->setMaxAge(60);
  203.             }
  204.         }
  205.         return $result;
  206.     }
  207.     #[ParamConverter("city"converter"city_converter")]
  208.     public function listByCity(ParameterBagInterface $parameterBagRequest $requestCity $citybool $subRequest false): Response
  209.     {
  210.         $page $this->getCurrentPageNumber();
  211.         if ($this->features->redirect_default_city_to_homepage() && false === $subRequest && $city->equals($parameterBag->get('default_city')) && $page 2) {
  212.             return $this->redirectToRoute('homepage', [], 301);
  213.         }
  214.         $specs $this->profileListSpecificationService->listByCity();
  215.         $response = new Response();
  216.         $result $this->paginatedListing($city'/city/{city}', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  217.         $homepageCityListingsBlock null;
  218.         if ($this->shouldShowHomepageCityListingsBlock($city$page$subRequest)) {
  219.             $homepageCityListingsBlock $this->homepageCityListingsBlockProvider->getForCity($city);
  220.         }
  221.         return $this->render('ProfileList/list.html.twig', [
  222.             'profiles' => $result,
  223.             'homepage_city_listings_block' => $homepageCityListingsBlock,
  224.             'recommendationSpec' => $specs->recommendationSpec(),
  225.         ], response$response);
  226.     }
  227.     /**
  228.      * @Feature("intim_moscow_listing")
  229.      */
  230.     #[Cache(maxage3600, public: true)]
  231.     public function listIntim(DefaultCityProvider $defaultCityProvider): Response
  232.     {
  233.         $city $defaultCityProvider->getDefaultCity();
  234.         $request $this->requestStack->getCurrentRequest();
  235.         $request?->attributes->set('city'$city);
  236.         $specs $this->profileListSpecificationService->listByCity();
  237.         $response = new Response();
  238.         $result $this->paginatedListing($city'/city/{city}/intim', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  239.         $result $this->shuffleProfilesOnPage($result);
  240.         $response->setMaxAge(3600);
  241.         return $this->render('ProfileList/list.html.twig', [
  242.             'profiles' => $result,
  243.             'city' => $city,
  244.             'recommendationSpec' => $specs->recommendationSpec(),
  245.         ], response$response);
  246.     }
  247.     #[ParamConverter("city"converter"city_converter")]
  248.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  249.     public function listByCounty(Request $requestCity $cityCounty $county): Response
  250.     {
  251.         if (!$city->hasCounty($county)) {
  252.             throw $this->createNotFoundException();
  253.         }
  254.         $specs $this->profileListSpecificationService->listByCounty($county);
  255.         $response = new Response();
  256.         $alternativeSpec Spec::orX(ProfileIsLocated::withinCounties($city$city->getCounties()->toArray()));
  257.         $result $this->paginatedListing($city'/city/{city}/county/{county}', ['city' => $city->getId(), 'county' => $county->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_COUNTY$response);
  258.         return $this->render('ProfileList/list.html.twig', [
  259.             'profiles' => $result,
  260.             'source' => $this->source,
  261.             'source_default' => self::RESULT_SOURCE_COUNTY,
  262.             'county' => $county,
  263.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  264.                 'city' => $city->getUriIdentity(),
  265.                 'county' => $county->getUriIdentity(),
  266.                 'page' => $this->getCurrentPageNumber()
  267.             ]),
  268.             'recommendationSpec' => $specs->recommendationSpec(),
  269.         ], response$response);
  270.     }
  271.     #[ParamConverter("city"converter"city_converter")]
  272.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  273.     public function listByDistrict(Request $requestCity $cityDistrict $district): Response
  274.     {
  275.         if (!$city->hasDistrict($district)) {
  276.             throw $this->createNotFoundException();
  277.         }
  278.         $specs $this->profileListSpecificationService->listByDistrict($district);
  279.         $response = new Response();
  280.         $alternativeSpec Spec::orX(ProfileIsLocated::withinDistricts($city$city->getDistricts()->toArray()));
  281.         $result $this->paginatedListing($city'/city/{city}/district/{district}', ['city' => $city->getId(), 'district' => $district->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_DISTRICT$response);
  282.         return $this->render('ProfileList/list.html.twig', [
  283.             'profiles' => $result,
  284.             'source' => $this->source,
  285.             'source_default' => self::RESULT_SOURCE_DISTRICT,
  286.             'district' => $district,
  287.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  288.                 'city' => $city->getUriIdentity(),
  289.                 'district' => $district->getUriIdentity(),
  290.                 'page' => $this->getCurrentPageNumber()
  291.             ]),
  292.             'recommendationSpec' => $specs->recommendationSpec(),
  293.         ], response$response);
  294.     }
  295.     /**
  296.      * @Feature("extra_category_without_prepayment")
  297.      */
  298.     #[ParamConverter("city"converter"city_converter")]
  299.     public function listWithoutPrepayment(Request $requestCity $city): Response
  300.     {
  301.         $listingData $this->profileListingDataCreator->getListingDataForFilter('listWithoutPrepayment', [], $city);
  302.         $specs $listingData['specs'];
  303.         $listingTypeName $listingData['listingTypeName'];
  304.         $response = new Response();
  305.         $result $this->paginatedListing($city'/city/{city}/category/without_prepayment', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  306.         $request->attributes->set('profiles_count'$result->totalCount());
  307.         $request->attributes->set('listingTypeName'$listingTypeName);
  308.         $request->attributes->set('city'$city);
  309.         return $this->render('ProfileList/list.html.twig', [
  310.             'profiles' => $result,
  311.             'source' => $this->source,
  312.             'source_default' => 'without_prepayment',
  313.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  314.                 'city' => $city->getUriIdentity(),
  315.                 'page' => $this->getCurrentPageNumber(),
  316.             ]),
  317.             'recommendationSpec' => $specs->recommendationSpec(),
  318.         ], response$response);
  319.     }
  320.     #[ParamConverter("city"converter"city_converter")]
  321.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  322.     public function listByStation(Request $requestCity $cityStation $station): Response
  323.     {
  324.         if (!$city->hasStation($station)) {
  325.             throw $this->createNotFoundException();
  326.         }
  327.         $specs $this->profileListSpecificationService->listByStation($station);
  328.         $response = new Response();
  329.         $result $this->paginatedListing($city'/city/{city}/station/{station}', ['city' => $city->getId(), 'station' => $station->getId()], $specs->spec(), nullnull$response);
  330.         $prevCount $result->count();
  331.         if (true === $this->features->station_page_add_profiles()) {
  332.             $spread $this->parameterBag->get('app.profile.station_page.added_profiles.spread');
  333.             $result $this->addSinglePageStationResults($result$city$station$spread ?: 5);
  334.         }
  335.         if (null !== $station->getDistrict()) {
  336.             $result $this->checkEmptyResultNotMasseur($result$citySpec::orX(ProfileIsLocated::nearStations($city$station->getDistrict()->getStations()->toArray())), self::RESULT_SOURCE_STATION);
  337.         } else {
  338.             $result $this->checkCityAndCountrySource($result$city);
  339.         }
  340.         if ($result->count() > $prevCount) {
  341.             $response?->setMaxAge(60);
  342.         }
  343.         return $this->render('ProfileList/list.html.twig', [
  344.             'profiles' => $result,
  345.             'source' => $this->source,
  346.             'source_default' => self::RESULT_SOURCE_STATION,
  347.             'station' => $station,
  348.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  349.                 'city' => $city->getUriIdentity(),
  350.                 'station' => $station->getUriIdentity(),
  351.                 'page' => $this->getCurrentPageNumber()
  352.             ]),
  353.             'recommendationSpec' => $specs->recommendationSpec(),
  354.         ], response$response);
  355.     }
  356.     private function addSinglePageStationResults(Page $resultCity $cityStation $stationint $spread): Page
  357.     {
  358.         if ($result->totalCount() >= $result->getCurrentLimit()) {
  359.             return $result;
  360.         }
  361.         $addedProfileIds $this->stationAddedProfilesCache->get(self::CACHE_ITEM_STATION_ADDED_PROFILES $station->getId(), function () use ($result$city$station$spread): array {
  362.             $currentSpread rand(0$spread);
  363.             $plannedTotalCount $result->getCurrentLimit() - $spread $currentSpread;
  364.             $result iterator_to_array($result->getIterator());
  365.             $originalProfileIds $this->extractProfileIds($result);
  366.             if ($station->getDistrict()) {
  367.                 $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinDistrict($station->getDistrict())), $plannedTotalCount);
  368.             }
  369.             if ($station->getDistrict()?->getCounty()) {
  370.                 $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinCounty($station->getDistrict()->getCounty())), $plannedTotalCount);
  371.             }
  372.             $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinCity($city)), $plannedTotalCount);
  373.             $result $this->extractProfileIds($result);
  374.             return array_diff($result$originalProfileIds);
  375.         });
  376.         $addedProfileIds array_slice($addedProfileIds0$result->getCurrentLimit() - $result->totalCount());
  377.         $originalProfiles iterator_to_array($result->getIterator());
  378.         $addedProfiles $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited($city, new ProfileIdIn($addedProfileIds), null, [Genders::FEMALE], count($addedProfileIds));
  379.         $newResult array_merge($originalProfiles$addedProfiles);
  380.         return new FakeORMQueryPage(01$result->getCurrentLimit(), count($newResult), $newResult);
  381.     }
  382.     private function addSinglePageResultsUptoAmount(array $resultCity $city, ?Filter $specsint $totalCount): array
  383.     {
  384.         $toAdd $totalCount count($result);
  385.         if ($toAdd 0) {
  386.             $currentResultIds $this->extractProfileIds($result);
  387.             $resultsToAdd $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited($city$specs, [new ProfileIdNotIn($currentResultIds)], [Genders::FEMALE], $toAdd);
  388.             $result array_merge($result$resultsToAdd);
  389.         }
  390.         return $result;
  391.     }
  392.     #[ParamConverter("city"converter"city_converter")]
  393.     public function listByStations(City $citystring $stationsStationRepository $stationRepository): Response
  394.     {
  395.         $stationIds explode(','$stations);
  396.         $stations $stationRepository->findBy(['uriIdentity' => $stationIds]);
  397.         $specs $this->profileListSpecificationService->listByStations($stations);
  398.         $response = new Response();
  399.         $result $this->paginatedListing($citynull, [], $specs->spec(), nullnull$response);
  400.         return $this->render('ProfileList/list.html.twig', [
  401.             'profiles' => $result,
  402.             'recommendationSpec' => $specs->recommendationSpec(),
  403.         ]);
  404.     }
  405.     #[ParamConverter("city"converter"city_converter")]
  406.     public function listApproved(Request $requestCity $city): Response
  407.     {
  408.         $specs $this->profileListSpecificationService->listApproved();
  409.         $response = new Response();
  410.         $result $this->paginatedListing($city'/city/{city}/approved', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  411.         $prevCount $result->count();
  412.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  413.             $this->source self::RESULT_SOURCE_WITH_COMMENTS;
  414.             $result $this->listRandomSinglePage($citynull, new ProfileHasComments(), nulltruefalse);
  415.             if ($result->count() == 0) {
  416.                 $this->source self::RESULT_SOURCE_WITH_VIDEO;
  417.                 $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  418.             }
  419.             if ($result->count() == 0) {
  420.                 $this->source self::RESULT_SOURCE_ELITE;
  421.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  422.             }
  423.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  424.         }
  425.         if ($result->count() > $prevCount) {
  426.             $response?->setMaxAge(60);
  427.         }
  428.         return $this->render('ProfileList/list.html.twig', [
  429.             'profiles' => $result,
  430.             'source' => $this->source,
  431.             'source_default' => self::RESULT_SOURCE_APPROVED,
  432.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  433.                 'city' => $city->getUriIdentity(),
  434.                 'page' => $this->getCurrentPageNumber()
  435.             ]),
  436.             'recommendationSpec' => $specs->recommendationSpec(),
  437.         ], response$response);
  438.     }
  439.     /**
  440.      * @Feature("extra_category_with_whatsapp")
  441.      */
  442.     #[ParamConverter("city"converter"city_converter")]
  443.     public function listWithWhatsapp(Request $requestCity $city): Response
  444.     {
  445.         $specs $this->profileListSpecificationService->listWithWhatsapp();
  446.         $response = new Response();
  447.         $result $this->paginatedListing($city'/city/{city}/category/whatsapp', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  448.         return $this->render('ProfileList/list.html.twig', [
  449.             'profiles' => $result,
  450.             'source' => $this->source,
  451.             'source_default' => self::RESULT_SOURCE_WITH_WHATSAPP,
  452.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  453.                 'city' => $city->getUriIdentity(),
  454.                 'page' => $this->getCurrentPageNumber(),
  455.             ]),
  456.             'recommendationSpec' => $specs->recommendationSpec(),
  457.         ], response$response);
  458.     }
  459.     /**
  460.      * @Feature("extra_category_with_telegram")
  461.      */
  462.     #[ParamConverter("city"converter"city_converter")]
  463.     public function listWithTelegram(Request $requestCity $city): Response
  464.     {
  465.         $specs $this->profileListSpecificationService->listWithTelegram();
  466.         $response = new Response();
  467.         $result $this->paginatedListing($city'/city/{city}/category/telegram', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  468.         return $this->render('ProfileList/list.html.twig', [
  469.             'profiles' => $result,
  470.             'source' => $this->source,
  471.             'source_default' => self::RESULT_SOURCE_WITH_TELEGRAM,
  472.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  473.                 'city' => $city->getUriIdentity(),
  474.                 'page' => $this->getCurrentPageNumber(),
  475.             ]),
  476.             'recommendationSpec' => $specs->recommendationSpec(),
  477.         ], response$response);
  478.     }
  479.     /**
  480.      * @Feature("extra_category_eighteen_years_old")
  481.      */
  482.     #[ParamConverter("city"converter"city_converter")]
  483.     public function listEighteenYearsOld(Request $requestCity $city): Response
  484.     {
  485.         $specs $this->profileListSpecificationService->listEighteenYearsOld();
  486.         $response = new Response();
  487.         $result $this->paginatedListing($city'/city/{city}/category/eighteen_years_old', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  488.         return $this->render('ProfileList/list.html.twig', [
  489.             'profiles' => $result,
  490.             'source' => $this->source,
  491.             'source_default' => self::RESULT_SOURCE_EIGHTEEN_YEARS_OLD,
  492.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  493.                 'city' => $city->getUriIdentity(),
  494.                 'page' => $this->getCurrentPageNumber(),
  495.             ]),
  496.             'recommendationSpec' => $specs->recommendationSpec(),
  497.         ], response$response);
  498.     }
  499.     /**
  500.      * @Feature("extra_category_rublevskie")
  501.      */
  502.     #[ParamConverter("city"converter"city_converter")]
  503.     public function listRublevskie(Request $requestCity $city): Response
  504.     {
  505.         if ($city->getUriIdentity() !== 'moscow') {
  506.             throw $this->createNotFoundException();
  507.         }
  508.         $specs $this->profileListSpecificationService->listRublevskie($city);
  509.         $response = new Response();
  510.         $result $this->paginatedListing($city'/city/{city}/category/rublevskie', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  511.         return $this->render('ProfileList/list.html.twig', [
  512.             'profiles' => $result,
  513.             'source' => $this->source,
  514.             'source_default' => 'rublevskie',
  515.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  516.                 'city' => $city->getUriIdentity(),
  517.                 'page' => $this->getCurrentPageNumber(),
  518.             ]),
  519.             'recommendationSpec' => $specs->recommendationSpec(),
  520.         ], response$response);
  521.     }
  522.     /**
  523.      * @Feature("extra_category_with_tattoo")
  524.      */
  525.     #[ParamConverter("city"converter"city_converter")]
  526.     public function listWithTattoo(Request $requestCity $city): Response
  527.     {
  528.         $specs $this->profileListSpecificationService->listWithTattoo();
  529.         $response = new Response();
  530.         $result $this->paginatedListing($city'/city/{city}/category/with_tattoo', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  531.         return $this->render('ProfileList/list.html.twig', [
  532.             'profiles' => $result,
  533.             'source' => $this->source,
  534.             'source_default' => self::RESULT_SOURCE_WITH_TATTOO,
  535.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  536.                 'city' => $city->getUriIdentity(),
  537.                 'page' => $this->getCurrentPageNumber(),
  538.             ]),
  539.             'recommendationSpec' => $specs->recommendationSpec(),
  540.         ], response$response);
  541.     }
  542.     #[ParamConverter("city"converter"city_converter")]
  543.     public function listWithComments(Request $requestCity $city): Response
  544.     {
  545.         $specs $this->profileListSpecificationService->listWithComments();
  546.         $response = new Response();
  547.         $result $this->paginatedListing($city'/city/{city}/with_comments', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  548.         $prevCount $result->count();
  549.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  550.             $this->source self::RESULT_SOURCE_APPROVED;
  551.             $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  552.             if ($result->count() == 0) {
  553.                 $this->source self::RESULT_SOURCE_WITH_VIDEO;
  554.                 $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  555.             }
  556.             if ($result->count() == 0) {
  557.                 $this->source self::RESULT_SOURCE_ELITE;
  558.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  559.             }
  560.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  561.         }
  562.         if ($result->count() > $prevCount) {
  563.             $response?->setMaxAge(60);
  564.         }
  565.         return $this->render('ProfileList/list.html.twig', [
  566.             'profiles' => $result,
  567.             'source' => $this->source,
  568.             'source_default' => self::RESULT_SOURCE_WITH_COMMENTS,
  569.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  570.                 'city' => $city->getUriIdentity(),
  571.                 'page' => $this->getCurrentPageNumber()
  572.             ]),
  573.             'recommendationSpec' => $specs->recommendationSpec(),
  574.         ], response$response);
  575.     }
  576.     /**
  577.      * @Feature("extra_category_with_piercing")
  578.      */
  579.     #[ParamConverter("city"converter"city_converter")]
  580.     public function listWithPiercing(Request $requestCity $city): Response
  581.     {
  582.         $specs $this->profileListSpecificationService->listWithPiercing();
  583.         $response = new Response();
  584.         $result $this->paginatedListing($city'/city/{city}/category/with_piercing', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  585.         return $this->render('ProfileList/list.html.twig', [
  586.             'profiles' => $result,
  587.             'source' => $this->source,
  588.             'source_default' => self::RESULT_SOURCE_WITH_PIERCING,
  589.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  590.                 'city' => $city->getUriIdentity(),
  591.                 'page' => $this->getCurrentPageNumber(),
  592.             ]),
  593.             'recommendationSpec' => $specs->recommendationSpec(),
  594.         ], response$response);
  595.     }
  596.     #[ParamConverter("city"converter"city_converter")]
  597.     public function listWithVideo(Request $requestCity $city): Response
  598.     {
  599.         $specs $this->profileListSpecificationService->listWithVideo();
  600.         $response = new Response();
  601.         $result $this->paginatedListing($city'/city/{city}/with_video', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  602.         $prevCount $result->count();
  603.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  604.             $this->source self::RESULT_SOURCE_APPROVED;
  605.             $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  606.             if ($result->count() == 0) {
  607.                 $this->source self::RESULT_SOURCE_WITH_COMMENTS;
  608.                 $result $this->listRandomSinglePage($citynull, new ProfileHasComments(), nulltruefalse);
  609.             }
  610.             if ($result->count() == 0) {
  611.                 $this->source self::RESULT_SOURCE_ELITE;
  612.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  613.             }
  614.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  615.         }
  616.         if ($result->count() > $prevCount) {
  617.             $response?->setMaxAge(60);
  618.         }
  619.         return $this->render('ProfileList/list.html.twig', [
  620.             'profiles' => $result,
  621.             'source' => $this->source,
  622.             'source_default' => self::RESULT_SOURCE_WITH_VIDEO,
  623.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  624.                 'city' => $city->getUriIdentity(),
  625.                 'page' => $this->getCurrentPageNumber()
  626.             ]),
  627.             'recommendationSpec' => $specs->recommendationSpec(),
  628.         ], response$response);
  629.     }
  630.      /**
  631.      * @Feature("extra_category_round_the_clock")
  632.      */
  633.     #[ParamConverter("city"converter"city_converter")]
  634.     public function listRoundTheClock(Request $requestCity $city): Response
  635.     {
  636.         $specs $this->profileListSpecificationService->listRoundTheClock();
  637.         $response = new Response();
  638.         $result $this->paginatedListing($city'/city/{city}/category/round_the_clock', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  639.         return $this->render('ProfileList/list.html.twig', [
  640.             'profiles' => $result,
  641.             'source' => $this->source,
  642.             'source_default' => self::RESULT_SOURCE_ROUND_THE_CLOCK,
  643.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  644.                 'city' => $city->getUriIdentity(),
  645.                 'page' => $this->getCurrentPageNumber(),
  646.             ]),
  647.             'recommendationSpec' => $specs->recommendationSpec(),
  648.         ], response$response);
  649.     }
  650.     /**
  651.      * @Feature("extra_category_for_two_hours")
  652.      */
  653.     #[ParamConverter("city"converter"city_converter")]
  654.     public function listForTwoHours(Request $requestCity $city): Response
  655.     {
  656.         $specs $this->profileListSpecificationService->listForTwoHours();
  657.         $response = new Response();
  658.         $result $this->paginatedListing($city'/city/{city}/category/for_two_hours', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  659.         return $this->render('ProfileList/list.html.twig', [
  660.             'profiles' => $result,
  661.             'source' => $this->source,
  662.             'source_default' => self::RESULT_SOURCE_FOR_TWO_HOURS,
  663.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  664.                 'city' => $city->getUriIdentity(),
  665.                 'page' => $this->getCurrentPageNumber(),
  666.             ]),
  667.             'recommendationSpec' => $specs->recommendationSpec(),
  668.         ], response$response);
  669.     }
  670.     /**
  671.      * @Feature("extra_category_for_hour")
  672.      */
  673.     #[ParamConverter("city"converter"city_converter")]
  674.     public function listForHour(Request $requestCity $city): Response
  675.     {
  676.         $specs $this->profileListSpecificationService->listForHour();
  677.         $response = new Response();
  678.         $result $this->paginatedListing($city'/city/{city}/category/for_hour', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  679.         return $this->render('ProfileList/list.html.twig', [
  680.             'profiles' => $result,
  681.             'source' => $this->source,
  682.             'source_default' => self::RESULT_SOURCE_FOR_HOUR,
  683.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  684.                 'city' => $city->getUriIdentity(),
  685.                 'page' => $this->getCurrentPageNumber(),
  686.             ]),
  687.             'recommendationSpec' => $specs->recommendationSpec(),
  688.         ], response$response);
  689.     }
  690.     /**
  691.      * @Feature("extra_category_express_program")
  692.      */
  693.     #[ParamConverter("city"converter"city_converter")]
  694.     public function listExpress(Request $requestCity $city): Response
  695.     {
  696.         $specs $this->profileListSpecificationService->listExpress();
  697.         $response = new Response();
  698.         $result $this->paginatedListing($city'/city/{city}/category/express', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  699.         return $this->render('ProfileList/list.html.twig', [
  700.             'profiles' => $result,
  701.             'source' => $this->source,
  702.             'source_default' => self::RESULT_SOURCE_EXPRESS_PROGRAM,
  703.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  704.                 'city' => $city->getUriIdentity(),
  705.                 'page' => $this->getCurrentPageNumber(),
  706.             ]),
  707.             'recommendationSpec' => $specs->recommendationSpec(),
  708.         ], response$response);
  709.     }
  710.     /**
  711.      * @Feature("extra_category_grandmothers")
  712.      */
  713.     #[ParamConverter("city"converter"city_converter")]
  714.     public function listGrandmothers(Request $requestCity $city): Response
  715.     {
  716.         $specs $this->profileListSpecificationService->listGrandmothers();
  717.         $response = new Response();
  718.         $result $this->paginatedListing($city'/city/{city}/category/grandmothers', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  719.         return $this->render('ProfileList/list.html.twig', [
  720.             'profiles' => $result,
  721.             'source' => $this->source,
  722.             'source_default' => 'grandmothers',
  723.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  724.                 'city' => $city->getUriIdentity(),
  725.                 'page' => $this->getCurrentPageNumber(),
  726.             ]),
  727.             'recommendationSpec' => $specs->recommendationSpec(),
  728.         ], response$response);
  729.     }
  730.         /**
  731.      * @Feature("extra_category_big_breast")
  732.      */
  733.     #[ParamConverter("city"converter"city_converter")]
  734.     public function listBigBreast(Request $requestCity $city): Response
  735.     {
  736.         $specs $this->profileListSpecificationService->listBigBreast();
  737.         $response = new Response();
  738.         $result $this->paginatedListing($city'/city/{city}/category/big_breast', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  739.         return $this->render('ProfileList/list.html.twig', [
  740.             'profiles' => $result,
  741.             'source' => $this->source,
  742.             'source_default' => 'big_breast',
  743.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  744.                 'city' => $city->getUriIdentity(),
  745.                 'page' => $this->getCurrentPageNumber(),
  746.             ]),
  747.             'recommendationSpec' => $specs->recommendationSpec(),
  748.         ], response$response);
  749.     }
  750.     /**
  751.      * @Feature("extra_category_very_skinny")
  752.      */
  753.     #[ParamConverter("city"converter"city_converter")]
  754.     public function listVerySkinny(Request $requestCity $city): Response
  755.     {
  756.         $specs $this->profileListSpecificationService->listVerySkinny();
  757.         $response = new Response();
  758.         $result $this->paginatedListing($city'/city/{city}/category/very_skinny', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  759.         return $this->render('ProfileList/list.html.twig', [
  760.             'profiles' => $result,
  761.             'source' => $this->source,
  762.             'source_default' => 'very_skinny',
  763.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  764.                 'city' => $city->getUriIdentity(),
  765.                 'page' => $this->getCurrentPageNumber(),
  766.             ]),
  767.             'recommendationSpec' => $specs->recommendationSpec(),
  768.         ], response$response);
  769.     }
  770.     /**
  771.      * @Feature("extra_category_small_ass")
  772.      */
  773.     #[ParamConverter("city"converter"city_converter")]
  774.     public function listSmallAss(Request $requestCity $city): Response
  775.     {
  776.         $specs $this->profileListSpecificationService->listSmallAss();
  777.         $response = new Response();
  778.         $result $this->paginatedListing($city'/city/{city}/category/small_ass', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  779.         return $this->render('ProfileList/list.html.twig', [
  780.             'profiles' => $result,
  781.             'source' => $this->source,
  782.             'source_default' => 'small_ass',
  783.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  784.                 'city' => $city->getUriIdentity(),
  785.                 'page' => $this->getCurrentPageNumber(),
  786.             ]),
  787.             'recommendationSpec' => $specs->recommendationSpec(),
  788.         ], response$response);
  789.     }
  790.     /**
  791.      * @Feature("extra_category_beautiful_prostitutes")
  792.      */
  793.     #[ParamConverter("city"converter"city_converter")]
  794.     public function listBeautifulProstitutes(Request $requestCity $city): Response
  795.     {
  796.         $specs $this->profileListSpecificationService->listBeautifulProstitutes();
  797.         $response = new Response();
  798.         $result $this->paginatedListing($city'/city/{city}/category/beautiful_prostitutes', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  799.         return $this->render('ProfileList/list.html.twig', [
  800.             'profiles' => $result,
  801.             'source' => $this->source,
  802.             'source_default' => 'beautiful_prostitutes',
  803.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  804.                 'city' => $city->getUriIdentity(),
  805.                 'page' => $this->getCurrentPageNumber(),
  806.             ]),
  807.             'recommendationSpec' => $specs->recommendationSpec(),
  808.         ], response$response);
  809.     }
  810.     /**
  811.      * @Feature("extra_category_without_intermediaries")
  812.      */
  813.     #[ParamConverter("city"converter"city_converter")]
  814.     public function listWithoutIntermediaries(Request $requestCity $city): Response
  815.     {
  816.         $specs $this->profileListSpecificationService->listWithoutIntermediaries();
  817.         $response = new Response();
  818.         $result $this->paginatedListing($city'/city/{city}/category/without_intermediaries', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  819.         return $this->render('ProfileList/list.html.twig', [
  820.             'profiles' => $result,
  821.             'source' => $this->source,
  822.             'source_default' => 'without_intermediaries',
  823.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  824.                 'city' => $city->getUriIdentity(),
  825.                 'page' => $this->getCurrentPageNumber(),
  826.             ]),
  827.             'recommendationSpec' => $specs->recommendationSpec(),
  828.         ], response$response);
  829.     }
  830.     /**
  831.      * @Feature("extra_category_intim_services")
  832.      */
  833.     #[ParamConverter("city"converter"city_converter")]
  834.     public function intimServices(Request $requestCity $city): Response
  835.     {
  836.         $servicesByGroup $this->serviceRepository->allIndexedByGroup();
  837.         return $this->render('ProfileList/intim_services.html.twig', [
  838.             'city' => $city,
  839.             'servicesByGroup' => $servicesByGroup,
  840.             'skipSetCurrentListingPage' => true,
  841.         ]);
  842.     }
  843.     /**
  844.      * @Feature("extra_category_outcall")
  845.      */
  846.     #[ParamConverter("city"converter"city_converter")]
  847.     public function listOutcall(Request $requestCity $city): Response
  848.     {
  849.         $specs $this->profileListSpecificationService->listByOutcall();
  850.         $response = new Response();
  851.         $result $this->paginatedListing($city'/city/{city}/category/outcall', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  852.         return $this->render('ProfileList/list.html.twig', [
  853.             'profiles' => $result,
  854.             'source' => $this->source,
  855.             'source_default' => 'outcall',
  856.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  857.                 'city' => $city->getUriIdentity(),
  858.                 'page' => $this->getCurrentPageNumber(),
  859.             ]),
  860.             'recommendationSpec' => $specs->recommendationSpec(),
  861.         ], response$response);
  862.     }
  863.     /**
  864.      * @Feature("extra_category_dwarfs")
  865.      */
  866.     #[ParamConverter("city"converter"city_converter")]
  867.     public function listDwarfs(Request $requestCity $city): Response
  868.     {
  869.         $specs $this->profileListSpecificationService->listDwarfs();
  870.         $response = new Response();
  871.         $result $this->paginatedListing($city'/city/{city}/category/dwarfs', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  872.         return $this->render('ProfileList/list.html.twig', [
  873.             'profiles' => $result,
  874.             'source' => $this->source,
  875.             'source_default' => 'dwarfs',
  876.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  877.                 'city' => $city->getUriIdentity(),
  878.                 'page' => $this->getCurrentPageNumber(),
  879.             ]),
  880.             'recommendationSpec' => $specs->recommendationSpec(),
  881.         ], response$response);
  882.     }
  883.     #[ParamConverter("city"converter"city_converter")]
  884.     public function listWithSelfie(Request $requestCity $city): Response
  885.     {
  886.         $specs $this->profileListSpecificationService->listWithSelfie();
  887.         $response = new Response();
  888.         $result $this->paginatedListing($city'/city/{city}/with_selfie', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  889.         $prevCount $result->count();
  890.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  891.             $this->source self::RESULT_SOURCE_WITH_VIDEO;
  892.             $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  893.             if ($result->count() == 0) {
  894.                 $this->source self::RESULT_SOURCE_APPROVED;
  895.                 $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  896.             }
  897.             if ($result->count() == 0) {
  898.                 $this->source self::RESULT_SOURCE_ELITE;
  899.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  900.             }
  901.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  902.         }
  903.         if ($result->count() > $prevCount) {
  904.             $response?->setMaxAge(60);
  905.         }
  906.         return $this->render('ProfileList/list.html.twig', [
  907.             'profiles' => $result,
  908.             'source' => $this->source,
  909.             'source_default' => self::RESULT_SOURCE_WITH_SELFIE,
  910.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  911.                 'city' => $city->getUriIdentity(),
  912.                 'page' => $this->getCurrentPageNumber()
  913.             ]),
  914.             'recommendationSpec' => $specs->recommendationSpec(),
  915.         ], response$response);
  916.     }
  917.     #[ParamConverter("city"converter"city_converter")]
  918.     #[Feature("extra_category_top_100")]
  919.     public function listTop100(Request $requestCity $city): Response
  920.     {
  921.         $specs $this->profileListSpecificationService->listApproved();
  922.         $result $this->top100ProfilesService->getSortedProfilesByVisits($city);
  923.         return $this->render('ProfileList/list.html.twig', [
  924.             'profiles' => $result,
  925.             'source' => self::RESULT_SOURCE_TOP_100,
  926.             'source_default' => self::RESULT_SOURCE_TOP_100,
  927.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  928.                 'city' => $city->getUriIdentity(),
  929.                 'page' => $this->getCurrentPageNumber(),
  930.             ]),
  931.             'recommendationSpec' => $specs->recommendationSpec(),
  932.         ]);
  933.     }
  934.     /**
  935.      * @Feature("extra_category_eromassage")
  936.      */
  937.     #[ParamConverter("city"converter"city_converter")]
  938.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  939.     public function listByCountyEromassage(Request $requestCity $cityCounty $county): Response
  940.     {
  941.         if (!$city->hasCounty($county)) {
  942.             throw $this->createNotFoundException();
  943.         }
  944.         $specs $this->profileListSpecificationService->listByCountyEromassage($county);
  945.         $response = new Response();
  946.         $result $this->paginatedListing(
  947.             $city,
  948.             '/city/{city}/county/{county}/eromassage',
  949.             ['city' => $city->getId(), 'county' => $county->getId()],
  950.             $specs->spec(),
  951.             null,
  952.             null,
  953.             $response
  954.         );
  955.         $request->attributes->set('profiles_count'$result->totalCount());
  956.         return $this->render('ProfileList/list.html.twig', [
  957.             'profiles' => $result,
  958.             'source' => $this->source,
  959.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  960.             'county' => $county,
  961.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  962.                 'city' => $city->getUriIdentity(),
  963.                 'county' => $county->getUriIdentity(),
  964.                 'page' => $this->getCurrentPageNumber(),
  965.             ]),
  966.             'recommendationSpec' => $specs->recommendationSpec(),
  967.         ], response$response);
  968.     }
  969.     /**
  970.      * @Feature("extra_category_eromassage")
  971.      */
  972.     #[ParamConverter("city"converter"city_converter")]
  973.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  974.     public function listByDistrictEromassage(Request $requestCity $cityDistrict $district): Response
  975.     {
  976.         if (!$city->hasDistrict($district)) {
  977.             throw $this->createNotFoundException();
  978.         }
  979.         $specs $this->profileListSpecificationService->listByDistrictEromassage($district);
  980.         $response = new Response();
  981.         $result $this->paginatedListing(
  982.             $city,
  983.             '/city/{city}/district/{district}/eromassage',
  984.             ['city' => $city->getId(), 'district' => $district->getId()],
  985.             $specs->spec(),
  986.             null,
  987.             null,
  988.             $response
  989.         );
  990.         $request->attributes->set('profiles_count'$result->totalCount());
  991.         return $this->render('ProfileList/list.html.twig', [
  992.             'profiles' => $result,
  993.             'source' => $this->source,
  994.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  995.             'district' => $district,
  996.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  997.                 'city' => $city->getUriIdentity(),
  998.                 'district' => $district->getUriIdentity(),
  999.                 'page' => $this->getCurrentPageNumber(),
  1000.             ]),
  1001.             'recommendationSpec' => $specs->recommendationSpec(),
  1002.         ], response$response);
  1003.     }
  1004.     /**
  1005.      * @Feature("extra_category_eromassage")
  1006.      */
  1007.     #[ParamConverter("city"converter"city_converter")]
  1008.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  1009.     public function listByStationEromassage(Request $requestCity $cityStation $station): Response
  1010.     {
  1011.         if (!$city->hasStation($station)) {
  1012.             throw $this->createNotFoundException();
  1013.         }
  1014.         $specs $this->profileListSpecificationService->listByStationEromassage($station);
  1015.         $response = new Response();
  1016.         $result $this->paginatedListing(
  1017.             $city,
  1018.             '/city/{city}/station/{station}/eromassage',
  1019.             ['city' => $city->getId(), 'station' => $station->getId()],
  1020.             $specs->spec(),
  1021.             null,
  1022.             null,
  1023.             $response
  1024.         );
  1025.         $request->attributes->set('profiles_count'$result->totalCount());
  1026.         return $this->render('ProfileList/list.html.twig', [
  1027.             'profiles' => $result,
  1028.             'source' => $this->source,
  1029.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  1030.             'station' => $station,
  1031.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1032.                 'city' => $city->getUriIdentity(),
  1033.                 'station' => $station->getUriIdentity(),
  1034.                 'page' => $this->getCurrentPageNumber(),
  1035.             ]),
  1036.             'recommendationSpec' => $specs->recommendationSpec(),
  1037.         ], response$response);
  1038.     }
  1039.     /**
  1040.      * @Feature("extra_category_verified")
  1041.      */
  1042.     #[ParamConverter("city"converter"city_converter")]
  1043.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  1044.     public function listByCountyVerified(Request $requestCity $cityCounty $county): Response
  1045.     {
  1046.         if (!$city->hasCounty($county)) {
  1047.             throw $this->createNotFoundException();
  1048.         }
  1049.         $specs $this->profileListSpecificationService->listByCountyVerified($county);
  1050.         $response = new Response();
  1051.         $result $this->paginatedListing(
  1052.             $city,
  1053.             '/city/{city}/county/{county}/proverennye',
  1054.             ['city' => $city->getId(), 'county' => $county->getId()],
  1055.             $specs->spec(),
  1056.             null,
  1057.             null,
  1058.             $response
  1059.         );
  1060.         $request->attributes->set('profiles_count'$result->totalCount());
  1061.         return $this->render('ProfileList/list.html.twig', [
  1062.             'profiles' => $result,
  1063.             'source' => $this->source,
  1064.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1065.             'county' => $county,
  1066.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1067.                 'city' => $city->getUriIdentity(),
  1068.                 'county' => $county->getUriIdentity(),
  1069.                 'page' => $this->getCurrentPageNumber(),
  1070.             ]),
  1071.             'recommendationSpec' => $specs->recommendationSpec(),
  1072.         ], response$response);
  1073.     }
  1074.     /**
  1075.      * @Feature("extra_category_verified")
  1076.      */
  1077.     #[ParamConverter("city"converter"city_converter")]
  1078.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  1079.     public function listByDistrictVerified(Request $requestCity $cityDistrict $district): Response
  1080.     {
  1081.         if (!$city->hasDistrict($district)) {
  1082.             throw $this->createNotFoundException();
  1083.         }
  1084.         $specs $this->profileListSpecificationService->listByDistrictVerified($district);
  1085.         $response = new Response();
  1086.         $result $this->paginatedListing(
  1087.             $city,
  1088.             '/city/{city}/district/{district}/proverennye',
  1089.             ['city' => $city->getId(), 'district' => $district->getId()],
  1090.             $specs->spec(),
  1091.             null,
  1092.             null,
  1093.             $response
  1094.         );
  1095.         $request->attributes->set('profiles_count'$result->totalCount());
  1096.         return $this->render('ProfileList/list.html.twig', [
  1097.             'profiles' => $result,
  1098.             'source' => $this->source,
  1099.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1100.             'district' => $district,
  1101.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1102.                 'city' => $city->getUriIdentity(),
  1103.                 'district' => $district->getUriIdentity(),
  1104.                 'page' => $this->getCurrentPageNumber(),
  1105.             ]),
  1106.             'recommendationSpec' => $specs->recommendationSpec(),
  1107.         ], response$response);
  1108.     }
  1109.     /**
  1110.      * @Feature("extra_category_verified")
  1111.      */
  1112.     #[ParamConverter("city"converter"city_converter")]
  1113.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  1114.     public function listByStationVerified(Request $requestCity $cityStation $station): Response
  1115.     {
  1116.         if (!$city->hasStation($station)) {
  1117.             throw $this->createNotFoundException();
  1118.         }
  1119.         $specs $this->profileListSpecificationService->listByStationVerified($station);
  1120.         $response = new Response();
  1121.         $result $this->paginatedListing(
  1122.             $city,
  1123.             '/city/{city}/station/{station}/proverennye',
  1124.             ['city' => $city->getId(), 'station' => $station->getId()],
  1125.             $specs->spec(),
  1126.             null,
  1127.             null,
  1128.             $response
  1129.         );
  1130.         $request->attributes->set('profiles_count'$result->totalCount());
  1131.         return $this->render('ProfileList/list.html.twig', [
  1132.             'profiles' => $result,
  1133.             'source' => $this->source,
  1134.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1135.             'station' => $station,
  1136.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1137.                 'city' => $city->getUriIdentity(),
  1138.                 'station' => $station->getUriIdentity(),
  1139.                 'page' => $this->getCurrentPageNumber(),
  1140.             ]),
  1141.             'recommendationSpec' => $specs->recommendationSpec(),
  1142.         ], response$response);
  1143.     }
  1144.     /**
  1145.      * @Feature("extra_category_cheap")
  1146.      */
  1147.     #[ParamConverter("city"converter"city_converter")]
  1148.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  1149.     public function listByCountyCheap(Request $requestCity $cityCounty $county): Response
  1150.     {
  1151.         if (!$city->hasCounty($county)) {
  1152.             throw $this->createNotFoundException();
  1153.         }
  1154.         $specs $this->profileListSpecificationService->listByCountyCheap($county);
  1155.         $response = new Response();
  1156.         $result $this->paginatedListing(
  1157.             $city,
  1158.             null,
  1159.             [],
  1160.             $specs->spec(),
  1161.             null,
  1162.             null,
  1163.             $response
  1164.         );
  1165.         $request->attributes->set('profiles_count'$result->totalCount());
  1166.         return $this->render('ProfileList/list.html.twig', [
  1167.             'profiles' => $result,
  1168.             'source' => $this->source,
  1169.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1170.             'county' => $county,
  1171.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1172.                 'city' => $city->getUriIdentity(),
  1173.                 'county' => $county->getUriIdentity(),
  1174.                 'page' => $this->getCurrentPageNumber(),
  1175.             ]),
  1176.             'recommendationSpec' => $specs->recommendationSpec(),
  1177.         ], response$response);
  1178.     }
  1179.     /**
  1180.      * @Feature("extra_category_cheap")
  1181.      */
  1182.     #[ParamConverter("city"converter"city_converter")]
  1183.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  1184.     public function listByDistrictCheap(Request $requestCity $cityDistrict $district): Response
  1185.     {
  1186.         if (!$city->hasDistrict($district)) {
  1187.             throw $this->createNotFoundException();
  1188.         }
  1189.         $specs $this->profileListSpecificationService->listByDistrictCheap($district);
  1190.         $response = new Response();
  1191.         $result $this->paginatedListing(
  1192.             $city,
  1193.             null,
  1194.             [],
  1195.             $specs->spec(),
  1196.             null,
  1197.             null,
  1198.             $response
  1199.         );
  1200.         $request->attributes->set('profiles_count'$result->totalCount());
  1201.         return $this->render('ProfileList/list.html.twig', [
  1202.             'profiles' => $result,
  1203.             'source' => $this->source,
  1204.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1205.             'district' => $district,
  1206.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1207.                 'city' => $city->getUriIdentity(),
  1208.                 'district' => $district->getUriIdentity(),
  1209.                 'page' => $this->getCurrentPageNumber(),
  1210.             ]),
  1211.             'recommendationSpec' => $specs->recommendationSpec(),
  1212.         ], response$response);
  1213.     }
  1214.     /**
  1215.      * @Feature("extra_category_cheap")
  1216.      */
  1217.     #[ParamConverter("city"converter"city_converter")]
  1218.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  1219.     public function listByStationCheap(Request $requestCity $cityStation $station): Response
  1220.     {
  1221.         if (!$city->hasStation($station)) {
  1222.             throw $this->createNotFoundException();
  1223.         }
  1224.         $specs $this->profileListSpecificationService->listByStationCheap($station);
  1225.         $response = new Response();
  1226.         $result $this->paginatedListing(
  1227.             $city,
  1228.             null,
  1229.             [],
  1230.             $specs->spec(),
  1231.             null,
  1232.             null,
  1233.             $response
  1234.         );
  1235.         $request->attributes->set('profiles_count'$result->totalCount());
  1236.         return $this->render('ProfileList/list.html.twig', [
  1237.             'profiles' => $result,
  1238.             'source' => $this->source,
  1239.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1240.             'station' => $station,
  1241.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1242.                 'city' => $city->getUriIdentity(),
  1243.                 'station' => $station->getUriIdentity(),
  1244.                 'page' => $this->getCurrentPageNumber(),
  1245.             ]),
  1246.             'recommendationSpec' => $specs->recommendationSpec(),
  1247.         ], response$response);
  1248.     }
  1249.     /**
  1250.      * @Feature("extra_category_mature")
  1251.      */
  1252.     #[ParamConverter("city"converter"city_converter")]
  1253.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  1254.     public function listByCountyMature(Request $requestCity $cityCounty $county): Response
  1255.     {
  1256.         if (!$city->hasCounty($county)) {
  1257.             throw $this->createNotFoundException();
  1258.         }
  1259.         $specs $this->profileListSpecificationService->listByCountyMature($county);
  1260.         $response = new Response();
  1261.         $result $this->paginatedListing(
  1262.             $city,
  1263.             null,
  1264.             [],
  1265.             $specs->spec(),
  1266.             null,
  1267.             null,
  1268.             $response
  1269.         );
  1270.         $request->attributes->set('profiles_count'$result->totalCount());
  1271.         return $this->render('ProfileList/list.html.twig', [
  1272.             'profiles' => $result,
  1273.             'source' => $this->source,
  1274.             'source_default' => self::RESULT_SOURCE_MATURE,
  1275.             'county' => $county,
  1276.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1277.                 'city' => $city->getUriIdentity(),
  1278.                 'county' => $county->getUriIdentity(),
  1279.                 'page' => $this->getCurrentPageNumber(),
  1280.             ]),
  1281.             'recommendationSpec' => $specs->recommendationSpec(),
  1282.         ], response$response);
  1283.     }
  1284.     /**
  1285.      * @Feature("extra_category_mature")
  1286.      */
  1287.     #[ParamConverter("city"converter"city_converter")]
  1288.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  1289.     public function listByDistrictMature(Request $requestCity $cityDistrict $district): Response
  1290.     {
  1291.         if (!$city->hasDistrict($district)) {
  1292.             throw $this->createNotFoundException();
  1293.         }
  1294.         $specs $this->profileListSpecificationService->listByDistrictMature($district);
  1295.         $response = new Response();
  1296.         $result $this->paginatedListing(
  1297.             $city,
  1298.             null,
  1299.             [],
  1300.             $specs->spec(),
  1301.             null,
  1302.             null,
  1303.             $response
  1304.         );
  1305.         $request->attributes->set('profiles_count'$result->totalCount());
  1306.         return $this->render('ProfileList/list.html.twig', [
  1307.             'profiles' => $result,
  1308.             'source' => $this->source,
  1309.             'source_default' => self::RESULT_SOURCE_MATURE,
  1310.             'district' => $district,
  1311.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1312.                 'city' => $city->getUriIdentity(),
  1313.                 'district' => $district->getUriIdentity(),
  1314.                 'page' => $this->getCurrentPageNumber(),
  1315.             ]),
  1316.             'recommendationSpec' => $specs->recommendationSpec(),
  1317.         ], response$response);
  1318.     }
  1319.     /**
  1320.      * @Feature("extra_category_mature")
  1321.      */
  1322.     #[ParamConverter("city"converter"city_converter")]
  1323.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  1324.     public function listByStationMature(Request $requestCity $cityStation $station): Response
  1325.     {
  1326.         if (!$city->hasStation($station)) {
  1327.             throw $this->createNotFoundException();
  1328.         }
  1329.         $specs $this->profileListSpecificationService->listByStationMature($station);
  1330.         $response = new Response();
  1331.         $result $this->paginatedListing(
  1332.             $city,
  1333.             null,
  1334.             [],
  1335.             $specs->spec(),
  1336.             null,
  1337.             null,
  1338.             $response
  1339.         );
  1340.         $request->attributes->set('profiles_count'$result->totalCount());
  1341.         return $this->render('ProfileList/list.html.twig', [
  1342.             'profiles' => $result,
  1343.             'source' => $this->source,
  1344.             'source_default' => self::RESULT_SOURCE_MATURE,
  1345.             'station' => $station,
  1346.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1347.                 'city' => $city->getUriIdentity(),
  1348.                 'station' => $station->getUriIdentity(),
  1349.                 'page' => $this->getCurrentPageNumber(),
  1350.             ]),
  1351.             'recommendationSpec' => $specs->recommendationSpec(),
  1352.         ], response$response);
  1353.     }
  1354.     /**
  1355.      * @Feature("extra_category_uzbek")
  1356.      */
  1357.     #[ParamConverter("city"converter"city_converter")]
  1358.     #[Entity("county"expr"repository.ofUriIdentityWithinCity(county, city)")]
  1359.     public function listByCountyUzbek(Request $requestCity $cityCounty $county): Response
  1360.     {
  1361.         if (!$city->hasCounty($county)) {
  1362.             throw $this->createNotFoundException();
  1363.         }
  1364.         $specs $this->profileListSpecificationService->listByCountyUzbek($county);
  1365.         $response = new Response();
  1366.         $result $this->paginatedListing(
  1367.             $city,
  1368.             '/city/{city}/county/{county}/uzbechki',
  1369.             ['city' => $city->getId(), 'county' => $county->getId()],
  1370.             $specs->spec(),
  1371.             null,
  1372.             null,
  1373.             $response
  1374.         );
  1375.         $request->attributes->set('profiles_count'$result->totalCount());
  1376.         return $this->render('ProfileList/list.html.twig', [
  1377.             'profiles' => $result,
  1378.             'source' => $this->source,
  1379.             'source_default' => self::RESULT_SOURCE_UZBEK,
  1380.             'county' => $county,
  1381.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1382.                 'city' => $city->getUriIdentity(),
  1383.                 'county' => $county->getUriIdentity(),
  1384.                 'page' => $this->getCurrentPageNumber(),
  1385.             ]),
  1386.             'recommendationSpec' => $specs->recommendationSpec(),
  1387.         ], response$response);
  1388.     }
  1389.     /**
  1390.      * @Feature("extra_category_uzbek")
  1391.      */
  1392.     #[ParamConverter("city"converter"city_converter")]
  1393.     #[Entity("district"expr"repository.ofUriIdentityWithinCity(district, city)")]
  1394.     public function listByDistrictUzbek(Request $requestCity $cityDistrict $district): Response
  1395.     {
  1396.         if (!$city->hasDistrict($district)) {
  1397.             throw $this->createNotFoundException();
  1398.         }
  1399.         $specs $this->profileListSpecificationService->listByDistrictUzbek($district);
  1400.         $response = new Response();
  1401.         $result $this->paginatedListing(
  1402.             $city,
  1403.             '/city/{city}/district/{district}/uzbechki',
  1404.             ['city' => $city->getId(), 'district' => $district->getId()],
  1405.             $specs->spec(),
  1406.             null,
  1407.             null,
  1408.             $response
  1409.         );
  1410.         $request->attributes->set('profiles_count'$result->totalCount());
  1411.         return $this->render('ProfileList/list.html.twig', [
  1412.             'profiles' => $result,
  1413.             'source' => $this->source,
  1414.             'source_default' => self::RESULT_SOURCE_UZBEK,
  1415.             'district' => $district,
  1416.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1417.                 'city' => $city->getUriIdentity(),
  1418.                 'district' => $district->getUriIdentity(),
  1419.                 'page' => $this->getCurrentPageNumber(),
  1420.             ]),
  1421.             'recommendationSpec' => $specs->recommendationSpec(),
  1422.         ], response$response);
  1423.     }
  1424.     /**
  1425.      * @Feature("extra_category_uzbek")
  1426.      */
  1427.     #[ParamConverter("city"converter"city_converter")]
  1428.     #[Entity("station"expr"repository.ofUriIdentityWithinCity(station, city)")]
  1429.     public function listByStationUzbek(Request $requestCity $cityStation $station): Response
  1430.     {
  1431.         if (!$city->hasStation($station)) {
  1432.             throw $this->createNotFoundException();
  1433.         }
  1434.         $specs $this->profileListSpecificationService->listByStationUzbek($station);
  1435.         $response = new Response();
  1436.         $result $this->paginatedListing(
  1437.             $city,
  1438.             '/city/{city}/station/{station}/uzbechki',
  1439.             ['city' => $city->getId(), 'station' => $station->getId()],
  1440.             $specs->spec(),
  1441.             null,
  1442.             null,
  1443.             $response
  1444.         );
  1445.         $request->attributes->set('profiles_count'$result->totalCount());
  1446.         return $this->render('ProfileList/list.html.twig', [
  1447.             'profiles' => $result,
  1448.             'source' => $this->source,
  1449.             'source_default' => self::RESULT_SOURCE_UZBEK,
  1450.             'station' => $station,
  1451.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1452.                 'city' => $city->getUriIdentity(),
  1453.                 'station' => $station->getUriIdentity(),
  1454.                 'page' => $this->getCurrentPageNumber(),
  1455.             ]),
  1456.             'recommendationSpec' => $specs->recommendationSpec(),
  1457.         ], response$response);
  1458.     }
  1459.     #[ParamConverter("city"converter"city_converter")]
  1460.     public function listByPrice(Request $requestCountryCurrencyResolver $countryCurrencyResolverCity $citystring $priceTypeint $minPrice nullint $maxPrice null): Response
  1461.     {
  1462.         $specs $this->profileListSpecificationService->listByPrice($city$priceType$minPrice$maxPrice);
  1463.         $response = new Response();
  1464.         $apiEndpoint in_array($priceType, ['low''high''elite']) ? '/city/{city}/price/'.$priceType null;
  1465.         $result $this->paginatedListing($city$apiEndpoint, ['city' => $city->getId()], $specs->spec(), nullnull$response);
  1466.         $prevCount $result->count();
  1467.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  1468.             $result $this->processListByPriceEmptyResult($result$city$priceType$minPrice$maxPrice);
  1469.         }
  1470.         if ($result->count() > $prevCount) {
  1471.             $response?->setMaxAge(60);
  1472.         }
  1473.         return $this->render('ProfileList/list.html.twig', [
  1474.             'profiles' => $result,
  1475.             'source' => $this->source,
  1476.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1477.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1478.                 'city' => $city->getUriIdentity(),
  1479.                 'priceType' => $priceType,
  1480.                 'minPrice' => $minPrice,
  1481.                 'maxPrice' => $maxPrice,
  1482.                 'page' => $this->getCurrentPageNumber()
  1483.             ]),
  1484.             'recommendationSpec' => $specs->recommendationSpec(),
  1485.         ], response$response);
  1486.     }
  1487.     private function processListByPriceEmptyResult(Page $resultCity $citystring $priceTypeint $minPrice nullint $maxPrice null)
  1488.     {
  1489.         if (!$this->features->fill_empty_profile_list())
  1490.             return $result;
  1491.         $this->source self::RESULT_SOURCE_BY_PARAMS;
  1492.         if ($this->countryCurrencyResolver->getCurrencyFor($city->getCountryCode()) == 'RUB') {
  1493.             if ($minPrice && $maxPrice) {
  1494.                 if ($minPrice == 2000 && $maxPrice == 3000) {
  1495.                     $priceSpec = [
  1496.                         ProfileWithApartmentsOneHourPrice::range(15002000),
  1497.                         ProfileWithApartmentsOneHourPrice::range(30004000),
  1498.                     ];
  1499.                 } else if ($minPrice == 3000 && $maxPrice == 4000) {
  1500.                     $priceSpec = [
  1501.                         ProfileWithApartmentsOneHourPrice::range(20003000),
  1502.                         ProfileWithApartmentsOneHourPrice::range(40005000),
  1503.                     ];
  1504.                 } else if ($minPrice == 4000 && $maxPrice == 5000) {
  1505.                     $priceSpec = [
  1506.                         ProfileWithApartmentsOneHourPrice::range(30004000),
  1507.                         ProfileWithApartmentsOneHourPrice::range(50006000),
  1508.                     ];
  1509.                 } else if ($minPrice == 5000 && $maxPrice == 6000) {
  1510.                     $priceSpec = [
  1511.                         ProfileWithApartmentsOneHourPrice::range(4000999999)
  1512.                     ];
  1513.                 } else {
  1514.                     $priceSpec = [
  1515.                         ProfileWithApartmentsOneHourPrice::range($minPrice$maxPrice)
  1516.                     ];
  1517.                 }
  1518.                 $result $this->listRandomSinglePage($citynullnull$priceSpectruefalse);
  1519.             } elseif ($maxPrice) {
  1520.                 if ($maxPrice == 500) {
  1521.                     $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(1500);
  1522.                     $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  1523.                     if ($result->count() == 0) {
  1524.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(15002000);
  1525.                         $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  1526.                     }
  1527.                 } else if ($maxPrice == 1500) {
  1528.                     $priceSpec ProfileWithApartmentsOneHourPrice::range(15002000);
  1529.                     $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  1530.                     if ($result->count() == 0) {
  1531.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(20003000);
  1532.                         $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  1533.                     }
  1534.                 }
  1535.             } else {
  1536.                 switch ($priceType) {
  1537.                     case 'not_expensive':
  1538.                         $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(2000);
  1539.                         break;
  1540.                     case 'high':
  1541.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(30006000);
  1542.                         break;
  1543.                     case 'low':
  1544.                         $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(2000);
  1545.                         break;
  1546.                     case 'elite':
  1547.                         $priceSpec ProfileWithApartmentsOneHourPrice::moreExpensiveThan(6000);
  1548.                         break;
  1549.                     default:
  1550.                         throw new \LogicException('Unknown price type');
  1551.                         break;
  1552.                 }
  1553.                 $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  1554.             }
  1555.         }
  1556.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1557.         return $result;
  1558.     }
  1559.     #[ParamConverter("city"converter"city_converter")]
  1560.     public function listByAge(Request $requestCity $citystring $ageTypeint $minAge nullint $maxAge null): Response
  1561.     {
  1562.         $specs $this->profileListSpecificationService->listByAge($ageType$minAge$maxAge);
  1563.         $response = new Response();
  1564.         $apiEndpoint in_array($ageType, ['young''old']) ? '/city/{city}/age/'.$ageType null;
  1565.         $result $this->paginatedListing($city$apiEndpoint, ['city' => $city->getId()], $specs->spec(), nullnull$response);
  1566.         $prevCount $result->count();
  1567.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  1568.             $filled $this->processListByAgeEmptyResult($result$city$ageType$minAge$maxAge);
  1569.             if ($filled)
  1570.                 $result $filled;
  1571.         }
  1572.         if ($result->count() > $prevCount) {
  1573.             $response?->setMaxAge(60);
  1574.         }
  1575.         return $this->render('ProfileList/list.html.twig', [
  1576.             'profiles' => $result,
  1577.             'source' => $this->source,
  1578.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1579.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1580.                 'city' => $city->getUriIdentity(),
  1581.                 'ageType' => $ageType,
  1582.                 'minAge' => $minAge,
  1583.                 'maxAge' => $maxAge,
  1584.                 'page' => $this->getCurrentPageNumber()
  1585.             ]),
  1586.             'recommendationSpec' => $specs->recommendationSpec(),
  1587.         ], response$response);
  1588.     }
  1589.     private function processListByAgeEmptyResult(Page $resultCity $citystring $ageTypeint $minAge nullint $maxAge null)
  1590.     {
  1591.         if (!$this->features->fill_empty_profile_list())
  1592.             return $result;
  1593.         $this->source self::RESULT_SOURCE_BY_PARAMS;
  1594.         if ($minAge && !$maxAge) {
  1595.             $startMinAge $minAge;
  1596.             do {
  1597.                 $startMinAge -= 2;
  1598.                 $ageSpec ProfileWithAge::olderThan($startMinAge);
  1599.                 $result $this->listRandomSinglePage($citynull$ageSpecnulltruefalse);
  1600.             } while ($result->count() == && $startMinAge >= 18);
  1601.         } else if ($ageType == 'young') {
  1602.             $startMaxAge 20;
  1603.             do {
  1604.                 $startMaxAge += 2;
  1605.                 $ageSpec ProfileWithAge::youngerThan($startMaxAge);
  1606.                 $result $this->listRandomSinglePage($citynull$ageSpecnulltruefalse);
  1607.             } while ($result->count() == && $startMaxAge <= 100);
  1608.         }
  1609.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1610.         return $result;
  1611.     }
  1612.     #[ParamConverter("city"converter"city_converter")]
  1613.     public function listByHeight(Request $requestCity $citystring $heightType): Response
  1614.     {
  1615.         $specs $this->profileListSpecificationService->listByHeight($heightType);
  1616.         $response = new Response();
  1617.         $result $this->paginatedListing($city'/city/{city}/height/'.$heightType, ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1618.         return $this->render('ProfileList/list.html.twig', [
  1619.             'profiles' => $result,
  1620.             'source' => $this->source,
  1621.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1622.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1623.                 'city' => $city->getUriIdentity(),
  1624.                 'heightType' => $heightType,
  1625.                 'page' => $this->getCurrentPageNumber()
  1626.             ]),
  1627.             'recommendationSpec' => $specs->recommendationSpec(),
  1628.         ], response$response);
  1629.     }
  1630.     #[ParamConverter("city"converter"city_converter")]
  1631.     public function listByBreastType(Request $requestCity $citystring $breastType): Response
  1632.     {
  1633.         if (null === $type BreastTypes::getValueByUriIdentity($breastType)) {
  1634.             throw $this->createNotFoundException();
  1635.         }
  1636.         $specs $this->profileListSpecificationService->listByBreastType($breastType);
  1637.         $response = new Response();
  1638.         $alternativeSpec $this->getORSpecForItemsArray(BreastTypes::getList(), function($item): ProfileWithBreastType {
  1639.             return new ProfileWithBreastType($item);
  1640.         });
  1641.         $result $this->paginatedListing($city'/city/{city}/breasttype/'.$type, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1642.         return $this->render('ProfileList/list.html.twig', [
  1643.             'profiles' => $result,
  1644.             'source' => $this->source,
  1645.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1646.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1647.                 'city' => $city->getUriIdentity(),
  1648.                 'breastType' => $breastType,
  1649.                 'page' => $this->getCurrentPageNumber()
  1650.             ]),
  1651.             'recommendationSpec' => $specs->recommendationSpec(),
  1652.         ], response$response);
  1653.     }
  1654.     #[ParamConverter("city"converter"city_converter")]
  1655.     public function listByHairColor(Request $requestCity $citystring $hairColor): Response
  1656.     {
  1657.         if (null === $color HairColors::getValueByUriIdentity($hairColor)) {
  1658.             throw $this->createNotFoundException();
  1659.         }
  1660.         $specs $this->profileListSpecificationService->listByHairColor($hairColor);
  1661.         $response = new Response();
  1662.         $alternativeSpec $this->getORSpecForItemsArray(HairColors::getList(), function($item): ProfileWithHairColor {
  1663.             return new ProfileWithHairColor($item);
  1664.         });
  1665.         $result $this->paginatedListing($city'/city/{city}/haircolor/'.$color, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1666.         return $this->render('ProfileList/list.html.twig', [
  1667.             'profiles' => $result,
  1668.             'source' => $this->source,
  1669.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1670.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1671.                 'city' => $city->getUriIdentity(),
  1672.                 'hairColor' => $hairColor,
  1673.                 'page' => $this->getCurrentPageNumber()
  1674.             ]),
  1675.             'recommendationSpec' => $specs->recommendationSpec(),
  1676.         ], response$response);
  1677.     }
  1678.     #[ParamConverter("city"converter"city_converter")]
  1679.     public function listByBodyType(Request $requestCity $citystring $bodyType): Response
  1680.     {
  1681.         if (null === $type BodyTypes::getValueByUriIdentity($bodyType)) {
  1682.             throw $this->createNotFoundException();
  1683.         }
  1684.         $specs $this->profileListSpecificationService->listByBodyType($bodyType);
  1685.         $response = new Response();
  1686.         $alternativeSpec $this->getORSpecForItemsArray(BodyTypes::getList(), function($item): ProfileWithBodyType {
  1687.             return new ProfileWithBodyType($item);
  1688.         });
  1689.         $result $this->paginatedListing($city'/city/{city}/bodytype/'.$type, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1690.         return $this->render('ProfileList/list.html.twig', [
  1691.             'profiles' => $result,
  1692.             'source' => $this->source,
  1693.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1694.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1695.                 'city' => $city->getUriIdentity(),
  1696.                 'bodyType' => $bodyType,
  1697.                 'page' => $this->getCurrentPageNumber()
  1698.             ]),
  1699.             'recommendationSpec' => $specs->recommendationSpec(),
  1700.         ], response$response);
  1701.     }
  1702.     #[ParamConverter("city"converter"city_converter")]
  1703.     public function listByPlace(Request $requestCity $citystring $placeTypestring $takeOutLocation null): Response
  1704.     {
  1705.         if ('take-out' === $placeType && null !== $takeOutLocation && null === TakeOutLocations::getValueByUriIdentity($takeOutLocation)) {
  1706.             throw $this->createNotFoundException();
  1707.         }
  1708.         $specs $this->profileListSpecificationService->listByPlace($placeType$takeOutLocation);
  1709.         if (null === $specs) {
  1710.             throw $this->createNotFoundException();
  1711.         }
  1712.         $response = new Response();
  1713.         $alternativeSpec $this->getORSpecForItemsArray(TakeOutLocations::getList(), function($item): ProfileIsProvidingTakeOut {
  1714.             return new ProfileIsProvidingTakeOut($item);
  1715.         });
  1716.         if ($placeType === 'take-out') {
  1717.             $alternativeSpec->orX(new ProfileHasApartments());
  1718.         }
  1719.         $apiEndpoint '/city/{city}/place/'.$placeType;
  1720.         if (null !== $takeOutLocation) {
  1721.             $apiEndpoint .= '/'.$takeOutLocation;
  1722.         }
  1723.         $result $this->paginatedListing($city$apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1724.         return $this->render('ProfileList/list.html.twig', [
  1725.             'profiles' => $result,
  1726.             'source' => $this->source,
  1727.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1728.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1729.                 'city' => $city->getUriIdentity(),
  1730.                 'placeType' => $placeType,
  1731.                 'takeOutLocation' => TakeOutLocations::getUriIdentity(TakeOutLocations::getValueByUriIdentity($takeOutLocation)),
  1732.                 'page' => $this->getCurrentPageNumber()
  1733.             ]),
  1734.             'recommendationSpec' => $specs->recommendationSpec(),
  1735.         ], response$response);
  1736.     }
  1737.     #[ParamConverter("city"converter"city_converter")]
  1738.     public function listByPrivateHaircut(Request $requestCity $citystring $privateHaircut): Response
  1739.     {
  1740.         if(null === $type PrivateHaircuts::getValueByUriIdentity($privateHaircut))
  1741.             throw $this->createNotFoundException();
  1742.         $specs $this->profileListSpecificationService->listByPrivateHaircut($privateHaircut);
  1743.         $response = new Response();
  1744.         $apiEndpoint '/city/{city}/privatehaircut/'.$type;
  1745.         $alternativeSpec $this->getORSpecForItemsArray(PrivateHaircuts::getList(), function($item): ProfileWithPrivateHaircut {
  1746.             return new ProfileWithPrivateHaircut($item);
  1747.         });
  1748.         $result $this->paginatedListing($city$apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1749.         return $this->render('ProfileList/list.html.twig', [
  1750.             'profiles' => $result,
  1751.             'source' => $this->source,
  1752.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1753.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1754.                 'city' => $city->getUriIdentity(),
  1755.                 'privateHaircut' => $privateHaircut,
  1756.                 'page' => $this->getCurrentPageNumber()
  1757.             ]),
  1758.             'recommendationSpec' => $specs->recommendationSpec(),
  1759.         ], response$response);
  1760.     }
  1761.     #[ParamConverter("city"converter"city_converter")]
  1762.     public function listByNationality(Request $requestCity $citystring $nationality): Response
  1763.     {
  1764.         if (null === $type Nationalities::getValueByUriIdentity($nationality))
  1765.             throw $this->createNotFoundException();
  1766.         $specs $this->profileListSpecificationService->listByNationality($nationality);
  1767.         $response = new Response();
  1768.         $alternativeSpec $this->getORSpecForItemsArray(Nationalities::getList(), function($item): ProfileWithNationality {
  1769.             return new ProfileWithNationality($item);
  1770.         });
  1771.         $apiEndpoint '/city/{city}/nationality/'.$type;
  1772.         $result $this->paginatedListing($city$apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_BY_PARAMS$response);
  1773.         return $this->render('ProfileList/list.html.twig', [
  1774.             'profiles' => $result,
  1775.             'source' => $this->source,
  1776.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1777.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1778.                 'city' => $city->getUriIdentity(),
  1779.                 'nationality' => $nationality,
  1780.                 'page' => $this->getCurrentPageNumber()
  1781.             ]),
  1782.             'recommendationSpec' => $specs->recommendationSpec(),
  1783.         ], response$response);
  1784.     }
  1785.     #[ParamConverter("city"converter"city_converter")]
  1786.     #[ParamConverter("service"options: ['mapping' => ['service' => 'uriIdentity']])]
  1787.     public function listByProvidedService(Request $requestCity $cityService $service): Response
  1788.     {
  1789.         $specs $this->profileListSpecificationService->listByProvidedService($service$city);
  1790.         $response = new Response();
  1791.         $sameGroupServices $this->serviceRepository->findBy(['group' => $service->getGroup()]);
  1792.         $alternativeSpec $this->getORSpecForItemsArray([$sameGroupServices], function($item): ProfileIsProvidingOneOfServices {
  1793.             return new ProfileIsProvidingOneOfServices($item);
  1794.         });
  1795.         $result $this->paginatedListing($city'/city/{city}/service/{service}', ['city' => $city->getId(), 'service' => $service->getId()], $specs->spec(), $alternativeSpecself::RESULT_SOURCE_SERVICE$response);
  1796.         return $this->render('ProfileList/list.html.twig', [
  1797.             'profiles' => $result,
  1798.             'source' => $this->source,
  1799.             'source_default' => self::RESULT_SOURCE_SERVICE,
  1800.             'service' => $service,
  1801.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1802.                 'city' => $city->getUriIdentity(),
  1803.                 'service' => $service->getUriIdentity(),
  1804.                 'page' => $this->getCurrentPageNumber()
  1805.             ]),
  1806.             'recommendationSpec' => $specs->recommendationSpec(),
  1807.         ], response$response);
  1808.     }
  1809.     /**
  1810.      * @Feature("has_archive_page")
  1811.      */
  1812.     #[ParamConverter("city"converter"city_converter")]
  1813.     public function listArchived(Request $requestCity $city): Response
  1814.     {
  1815.         $result $this->profileList->list($citynullnullnullfalsenullProfileList::ORDER_BY_UPDATED);
  1816.         return $this->render('ProfileList/list.html.twig', [
  1817.             'profiles' => $result,
  1818.             'recommendationSpec' => new \App\Specification\ElasticSearch\ProfileIsNotArchived(), //ProfileIsArchived, согласно https://redminez.net/issues/28305 в реках выводятся неарзивные
  1819.         ]);
  1820.     }
  1821.     #[ParamConverter("city"converter"city_converter")]
  1822.     public function listNew(City $cityint $weeks 2): Response
  1823.     {
  1824.         $specs $this->profileListSpecificationService->listNew($weeks);
  1825.         $response = new Response();
  1826.         $result $this->paginatedListing($city'/city/{city}/recent', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  1827.         return $this->render('ProfileList/list.html.twig', [
  1828.             'profiles' => $result,
  1829.             'recommendationSpec' => $specs->recommendationSpec(),
  1830.         ], response$response);
  1831.     }
  1832.     #[ParamConverter("city"converter"city_converter")]
  1833.     public function listByNoRetouch(City $city): Response
  1834.     {
  1835.         $specs $this->profileListSpecificationService->listByNoRetouch();
  1836.         $response = new Response();
  1837.         $result $this->paginatedListing($city'/city/{city}/noretouch', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1838.         return $this->render('ProfileList/list.html.twig', [
  1839.             'profiles' => $result,
  1840.             'profiles_count' => $result->count(),
  1841.             'source' => $this->source,
  1842.             'recommendationSpec' => $specs->recommendationSpec(),
  1843.         ], response$response);
  1844.     }
  1845.     #[ParamConverter("city"converter"city_converter")]
  1846.     public function listByNice(City $city): Response
  1847.     {
  1848.         $specs $this->profileListSpecificationService->listByNice();
  1849.         $response = new Response();
  1850.         $result $this->paginatedListing($city'/city/{city}/nice', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1851.         return $this->render('ProfileList/list.html.twig', [
  1852.             'profiles' => $result,
  1853.             'source' => $this->source,
  1854.             'recommendationSpec' => $specs->recommendationSpec(),
  1855.         ], response$response);
  1856.     }
  1857.     #[ParamConverter("city"converter"city_converter")]
  1858.     public function listByOnCall(City $city): Response
  1859.     {
  1860.         $specs $this->profileListSpecificationService->listByOnCall();
  1861.         $response = new Response();
  1862.         $result $this->paginatedListing($city'/city/{city}/oncall', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1863.         return $this->render('ProfileList/list.html.twig', [
  1864.             'profiles' => $result,
  1865.             'source' => $this->source,
  1866.             'recommendationSpec' => $specs->recommendationSpec(),
  1867.         ], response$response);
  1868.     }
  1869.     #[ParamConverter("city"converter"city_converter")]
  1870.     public function listForNight(City $city): Response
  1871.     {
  1872.         $specs $this->profileListSpecificationService->listForNight();
  1873.         $response = new Response();
  1874.         $result $this->paginatedListing($city'/city/{city}/fornight', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1875.         return $this->render('ProfileList/list.html.twig', [
  1876.             'profiles' => $result,
  1877.             'source' => $this->source,
  1878.             'recommendationSpec' => $specs->recommendationSpec(),
  1879.         ], response$response);
  1880.     }
  1881.     private function getSpecForEliteGirls(City $city): Filter
  1882.     {
  1883.         $minPrice $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  1884.             'RUB' => 5000,
  1885.             'UAH' => 1500,
  1886.             'USD' => 100,
  1887.             'EUR' => 130,
  1888.         ]);
  1889.         return new ProfileIsElite($minPrice);
  1890.     }
  1891.     private function getElasticSearchSpecForEliteGirls(City $city): ISpecification
  1892.     {
  1893.         $minPrice $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  1894.             'RUB' => 5000,
  1895.             'UAH' => 1500,
  1896.             'USD' => 100,
  1897.             'EUR' => 130,
  1898.         ]);
  1899.         return new \App\Specification\ElasticSearch\ProfileIsElite($minPrice);
  1900.     }
  1901.     #[ParamConverter("city"converter"city_converter")]
  1902.     public function listForEliteGirls(CountryCurrencyResolver $countryCurrencyResolverRequest $requestCity $city): Response
  1903.     {
  1904.         $specs $this->profileListSpecificationService->listForEliteGirls($city);
  1905.         $response = new Response();
  1906.         $result $this->paginatedListing($city'/city/{city}/elite', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  1907.         $prevCount $result->count();
  1908.         if ($this->features->fill_empty_profile_list() && $result->count() == 0) {
  1909.             $prices = [
  1910.                 'RUB' => 5000,
  1911.                 'UAH' => 1500,
  1912.                 'USD' => 100,
  1913.                 'EUR' => 130,
  1914.             ];
  1915.             $currency $countryCurrencyResolver->getCurrencyFor($city->getCountryCode());
  1916.             if (isset($prices[$currency])) {
  1917.                 $minPrice $prices[$currency];
  1918.                 switch ($currency) {
  1919.                     case 'RUB':
  1920.                         $diff 1000;
  1921.                         break;
  1922.                     case 'UAH':
  1923.                         $diff 500;
  1924.                         break;
  1925.                     case 'USD':
  1926.                     case 'EUR':
  1927.                         $diff 20;
  1928.                         break;
  1929.                     default:
  1930.                         throw new \LogicException('Unexpected currency code');
  1931.                 }
  1932.                 while ($minPrice >= $diff) {
  1933.                     $minPrice -= $diff;
  1934.                     $result $this->listRandomSinglePage($citynullProfileWithApartmentsOneHourPrice::moreExpensiveThan($minPrice), nulltruefalse);
  1935.                     if ($result->count() > 0) {
  1936.                         $this->source self::RESULT_SOURCE_BY_PARAMS;
  1937.                         break;
  1938.                     }
  1939.                 }
  1940.                 $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1941.             }
  1942.         }
  1943.         if ($result->count() > $prevCount) {
  1944.             $response?->setMaxAge(60);
  1945.         }
  1946.         return $this->render('ProfileList/list.html.twig', [
  1947.             'profiles' => $result,
  1948.             'source' => $this->source,
  1949.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1950.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1951.                 'city' => $city->getUriIdentity(),
  1952.                 'page' => $this->getCurrentPageNumber()
  1953.             ]),
  1954.             'recommendationSpec' => $specs->recommendationSpec(),
  1955.         ], response$response);
  1956.     }
  1957.     #[ParamConverter("city"converter"city_converter")]
  1958.     public function listForRealElite(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1959.     {
  1960.         $specs $this->profileListSpecificationService->listForRealElite($city);
  1961.         $response = new Response();
  1962.         $result $this->paginatedListing($city'/city/{city}/realelite', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1963.         return $this->render('ProfileList/list.html.twig', [
  1964.             'profiles' => $result,
  1965.             'source' => $this->source,
  1966.             'recommendationSpec' => $specs->recommendationSpec(),
  1967.         ], response$response);
  1968.     }
  1969.     #[ParamConverter("city"converter"city_converter")]
  1970.     public function listForVipPros(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1971.     {
  1972.         $specs $this->profileListSpecificationService->listForVipPros($city);
  1973.         $response = new Response();
  1974.         $result $this->paginatedListing($city'/city/{city}/vip', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1975.         return $this->render('ProfileList/list.html.twig', [
  1976.             'profiles' => $result,
  1977.             'source' => $this->source,
  1978.             'recommendationSpec' => $specs->recommendationSpec(),
  1979.         ], response$response);
  1980.     }
  1981.     #[ParamConverter("city"converter"city_converter")]
  1982.     public function listForVipIndividual(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1983.     {
  1984.         $specs $this->profileListSpecificationService->listForVipIndividual($city);
  1985.         $response = new Response();
  1986.         $result $this->paginatedListing($city'/city/{city}/vipindi', ['city' => $city->getId()], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1987.         return $this->render('ProfileList/list.html.twig', [
  1988.             'profiles' => $result,
  1989.             'source' => $this->source,
  1990.             'recommendationSpec' => $specs->recommendationSpec(),
  1991.         ], response$response);
  1992.     }
  1993.     #[ParamConverter("city"converter"city_converter")]
  1994.     public function listForVipGirlsCity(City $city): Response
  1995.     {
  1996.         $specs $this->profileListSpecificationService->listForVipGirlsCity($city);
  1997.         $response = new Response();
  1998.         $result $this->paginatedListing($citynull, [], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  1999.         return $this->render('ProfileList/list.html.twig', [
  2000.             'profiles' => $result,
  2001.             'source' => $this->source,
  2002.             'recommendationSpec' => $specs->recommendationSpec(),
  2003.         ], response$response);
  2004.     }
  2005.     #[ParamConverter("city"converter"city_converter")]
  2006.     public function listOfGirlfriends(City $city): Response
  2007.     {
  2008.         $specs $this->profileListSpecificationService->listOfGirlfriends();
  2009.         $response = new Response();
  2010.         $result $this->paginatedListing($citynull, [], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  2011.         return $this->render('ProfileList/list.html.twig', [
  2012.             'profiles' => $result,
  2013.             'source' => $this->source,
  2014.             'recommendationSpec' => $specs->recommendationSpec(),
  2015.         ]);
  2016.     }
  2017.     #[ParamConverter("city"converter"city_converter")]
  2018.     public function listOfMostExpensive(City $city): Response
  2019.     {
  2020.         $specs $this->profileListSpecificationService->listOfMostExpensive($city);
  2021.         $response = new Response();
  2022.         $result $this->paginatedListing($citynull, [], $specs->spec(), nullself::RESULT_SOURCE_CITY$response);
  2023.         return $this->render('ProfileList/list.html.twig', [
  2024.             'profiles' => $result,
  2025.             'source' => $this->source,
  2026.             'recommendationSpec' => $specs->recommendationSpec(),
  2027.         ]);
  2028.     }
  2029.     #[ParamConverter("city"converter"city_converter")]
  2030.     public function listBdsm(City $cityServiceRepository $serviceRepositoryParameterBagInterface $parameterBag): Response
  2031.     {
  2032.         $specs $this->profileListSpecificationService->listBdsm();
  2033.         $response = new Response();
  2034.         $result $this->paginatedListing($city'/city/{city}/bdsm', ['city' => $city->getId()], $specs->spec(), nullnull$response);
  2035.         return $this->render('ProfileList/list.html.twig', [
  2036.             'profiles' => $result,
  2037.             'recommendationSpec' => $specs->recommendationSpec(),
  2038.         ], response$response);
  2039.     }
  2040.     #[ParamConverter("city"converter"city_converter")]
  2041.     public function listByGender(City $citystring $genderDefaultCityProvider $defaultCityProvider): Response
  2042.     {
  2043.         if ($city->getId() != $defaultCityProvider->getDefaultCity()->getId()) {
  2044.             throw $this->createNotFoundException();
  2045.         }
  2046.         if (null === Genders::getValueByUriIdentity($gender))
  2047.             throw $this->createNotFoundException();
  2048.         $specs $this->profileListSpecificationService->listByGender($gender);
  2049.         $response = new Response();
  2050.         $result $this->paginatedListing($citynull, [], $specs->spec(), nullnull$response);
  2051.         return $this->render('ProfileList/list.html.twig', [
  2052.             'profiles' => $result,
  2053.             'recommendationSpec' => $specs->recommendationSpec(),
  2054.         ]);
  2055.     }
  2056.     protected function checkCityAndCountrySource(Page $resultCity $city): Page
  2057.     {
  2058.         if (($result && $result->count() != 0) || false == $this->features->fill_empty_profile_list())
  2059.             return $result;
  2060.         $this->source self::RESULT_SOURCE_CITY;
  2061.         $result $this->listRandomSinglePage($citynullnullnulltruefalse);
  2062.         if ($result->count() == 0) {
  2063.             $this->source self::RESULT_SOURCE_COUNTRY;
  2064.             $result $this->listRandomSinglePage($city$city->getCountryCode(), nullnulltruefalse);
  2065.         }
  2066.         return $result;
  2067.     }
  2068.     protected function checkEmptyResultNotMasseur(Page $resultCity $city, ?OrX $alternativeSpecstring $source): Page
  2069.     {
  2070.         if ($result->count() != || false == $this->features->fill_empty_profile_list())
  2071.             return $result;
  2072.         if (null != $alternativeSpec) {
  2073.             $this->source $source;
  2074.             $result $this->listRandomSinglePage($citynull$alternativeSpecnulltruefalse);
  2075.         }
  2076.         if ($result->count() == 0)
  2077.             $result $this->checkCityAndCountrySource($result$city);
  2078.         return $result;
  2079.     }
  2080.     /**
  2081.      * Сейчас не используется, решили доставать их всех соседних подкатегорий разом.
  2082.      * Пока оставил, вдруг передумают.
  2083.      * @deprecated
  2084.      */
  2085.     public function listByNextSimilarCategories(callable $listMethod$requestCategory, array $similarItems): ORMQueryResult
  2086.     {
  2087.         $similarItems array_filter($similarItems, function ($item) use ($requestCategory): bool {
  2088.             return $item != $requestCategory;
  2089.         });
  2090.         //shuffle($similarItems);
  2091.         $item null;
  2092.         $result null;
  2093.         do {
  2094.             $item $item == null current($similarItems) : next($similarItems);
  2095.             if (false === $item)
  2096.                 return $result;
  2097.             $result $listMethod($item);
  2098.         } while ($result->count() == 0);
  2099.         return $result;
  2100.     }
  2101.     private function shouldShowHomepageCityListingsBlock(City $cityint $pagebool $subRequest): bool
  2102.     {
  2103.         if ($page !== 1) {
  2104.             return false;
  2105.         }
  2106.         if ($subRequest) {
  2107.             return true;
  2108.         }
  2109.         return !$city->equals($this->parameterBag->get('default_city'));
  2110.     }
  2111.     protected function getCurrentPageNumber(): int
  2112.     {
  2113.         $page = (int) $this->requestStack->getCurrentRequest()?->get($this->pageParameter1);
  2114.         if ($page 1) {
  2115.             $page 1;
  2116.         }
  2117.         return $page;
  2118.     }
  2119.     protected function render(string $view, array $parameters = [], Response $response null): Response
  2120.     {
  2121.         $this->listingService->setCurrentListingPage($parameters['profiles']);
  2122.         $requestAttrs $this->requestStack->getCurrentRequest();
  2123.         $listing $requestAttrs->get('_controller');
  2124.         $listing is_array($listing) ? $listing[count($listing) - 1] : $listing;
  2125.         $listing preg_replace('/[^:]+::/'''$listing);
  2126.         $listingParameters $requestAttrs->get('_route_params');
  2127.         $listingParameters is_array($listingParameters) ? $listingParameters : [];
  2128.         $mainRequestHasPageParam = isset(($this->requestStack->getMainRequest()->get('_route_params') ?? [])['page']);
  2129.         if ($this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
  2130.             $view = (
  2131.                 str_starts_with($listing'list')
  2132.                 && 'ProfileList/list.html.twig' === $view
  2133.                 && $mainRequestHasPageParam //isset($listingParameters['page'])
  2134.             )
  2135.                 ? 'ProfileList/list.profiles.html.twig'
  2136.                 $view;
  2137.             return $this->prepareForXhr(parent::render($view$parameters$response));
  2138.             //return $this->getJSONResponse($parameters);
  2139.         } else {
  2140.             $parameters array_merge($parameters, [
  2141.                 'listing' => $listing,
  2142.                 'listing_parameters' => $listingParameters,
  2143.             ]);
  2144.             return parent::render($view$parameters$response);
  2145.         }
  2146.     }
  2147.     private function listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited(
  2148.         City $city,
  2149.         ?Filter $spec,
  2150.         array $additionalSpecs null,
  2151.         array $genders = [Genders::FEMALE],
  2152.         int $limit 0,
  2153.     ): array|Page {
  2154.         return $this->profileList->listActiveWithinCityOrderedByStatusWithSpecLimited($city$spec$additionalSpecs$genderstrue$limit);
  2155.     }
  2156.     private function listRandomSinglePage(
  2157.         City $city,
  2158.         ?string $country,
  2159.         ?Filter $spec,
  2160.         ?array $additionalSpecs,
  2161.         bool $active,
  2162.         ?bool $masseur false,
  2163.         array $genders = [Genders::FEMALE]
  2164.     ): Page {
  2165.         return $this->profileList->listRandom($city$country$spec$additionalSpecs$active$masseur$genderstrue);
  2166.     }
  2167.     private function shuffleProfilesOnPage(Page $result): Page
  2168.     {
  2169.         $profiles iterator_to_array($result->getIterator());
  2170.         if(count($profiles) > 1) {
  2171.             shuffle($profiles);
  2172.         }
  2173.         return new FakeORMQueryPage(
  2174.             $result->getCurrentOffset(),
  2175.             $result->getCurrentPage(),
  2176.             $result->getCurrentLimit(),
  2177.             $result->totalCount(),
  2178.             $profiles
  2179.         );
  2180.     }
  2181.     /**
  2182.      * Достает из списка анкет их id с учетом совместимости разных форматов данных
  2183.      */
  2184.     private function extractProfileIds(array $profiles): array
  2185.     {
  2186.         $ids array_map(static function ($item) {
  2187.             /**
  2188.              * - array - данные из микросервиса ротации через API
  2189.              * - Profile::getId() - полноценная сущность анкеты
  2190.              * - ProfileListingReadModel::$id - read-model анкеты
  2191.              */
  2192.             return is_array($item) ? $item['id'] : ($item?->id ?? $item?->getId());
  2193.         }, $profiles);
  2194.         return array_filter($ids); // remove null values
  2195.     }
  2196. //    protected function getJSONResponse(array $parameters)
  2197. //    {
  2198. //        $request = $this->request;
  2199. //        $data = json_decode($request->getContent(), true);
  2200. //
  2201. //        $imageSize = !empty($data['imageSize']) ? $data['imageSize'] : "357x500";
  2202. //
  2203. //        /** @var FakeORMQueryPage $queryPage */
  2204. //        $queryPage = $parameters['profiles'];
  2205. //
  2206. //        $profiles = array_map(function(ProfileListingReadModel $profile) use ($imageSize) {
  2207. //            $profile->stations = array_values($profile->stations);
  2208. //            $profile->avatar['path'] = $this->responsiveAssetsService->getResponsiveImageUrl($profile->avatar['path'], 'profile_media', $imageSize, 'jpg');
  2209. //            $profile->uri = $this->generateUrl('profile_preview.page', ['city' => $profile->city->uriIdentity, 'profile' => $profile->uriIdentity]);
  2210. //            return $profile;
  2211. //        }, $queryPage->getArray());
  2212. //
  2213. //        return new JsonResponse([
  2214. //            'profiles' => $profiles,
  2215. //            'currentPage' => $queryPage->getCurrentPage(),
  2216. //        ], Response::HTTP_OK);
  2217. //    }
  2218. }