src/Controller/ProfileListController.php line 708

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