src/Controller/ProfileListController.php line 319

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