src/Controller/ProfileListController.php line 456

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\ListingRotationApi;
  28. use App\Service\ListingService;
  29. use App\Service\ProfileList;
  30. use App\Service\ProfileListingDataCreator;
  31. use App\Service\ProfileListSpecificationService;
  32. use App\Service\ProfileFilterService;
  33. use App\Service\Top100ProfilesService;
  34. use App\Specification\ElasticSearch\ISpecification;
  35. use App\Specification\Profile\ProfileHasApartments;
  36. use App\Specification\Profile\ProfileHasComments;
  37. use App\Specification\Profile\ProfileHasVideo;
  38. use App\Specification\Profile\ProfileIdIn;
  39. use App\Specification\Profile\ProfileIdINOrderedByINValues;
  40. use App\Specification\Profile\ProfileIdNotIn;
  41. use App\Specification\Profile\ProfileIsApproved;
  42. use App\Specification\Profile\ProfileIsElite;
  43. use App\Specification\Profile\ProfileIsLocated;
  44. use App\Specification\Profile\ProfileIsProvidingOneOfServices;
  45. use App\Specification\Profile\ProfileIsProvidingTakeOut;
  46. use App\Specification\Profile\ProfileWithAge;
  47. use App\Specification\Profile\ProfileWithBodyType;
  48. use App\Specification\Profile\ProfileWithBreastType;
  49. use App\Specification\Profile\ProfileWithHairColor;
  50. use App\Specification\Profile\ProfileWithNationality;
  51. use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
  52. use App\Specification\Profile\ProfileWithPrivateHaircut;
  53. use Flagception\Bundle\FlagceptionBundle\Annotations\Feature;
  54. use Happyr\DoctrineSpecification\Filter\Filter;
  55. use Happyr\DoctrineSpecification\Logic\OrX;
  56. use Porpaginas\Doctrine\ORM\ORMQueryResult;
  57. use Porpaginas\Page;
  58. use Psr\Cache\CacheItemPoolInterface;
  59. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  60. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
  61. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  62. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  63. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  64. use Symfony\Component\HttpFoundation\Request;
  65. use Happyr\DoctrineSpecification\Spec;
  66. use Symfony\Component\HttpFoundation\RequestStack;
  67. use Symfony\Component\HttpFoundation\Response;
  68. /**
  69.  * @see \App\Console\Export\ExportRotationListingsConfigCommand for listing API endpoints
  70.  */
  71. #[Cache(maxage60, public: true)]
  72. class ProfileListController extends AbstractController
  73. {
  74.     use ExtendedPaginationTrait;
  75.     use SpecTrait;
  76.     use ProfileMinPriceTrait;
  77.     use ResponseTrait;
  78.     const ENTRIES_ON_PAGE 36;
  79.     const RESULT_SOURCE_COUNTY 'county';
  80.     const RESULT_SOURCE_DISTRICT 'district';
  81.     const RESULT_SOURCE_STATION 'station';
  82.     const RESULT_SOURCE_APPROVED 'approved';
  83.     const RESULT_SOURCE_WITH_COMMENTS 'with_comments';
  84.     const RESULT_SOURCE_WITH_VIDEO 'with_video';
  85.     const RESULT_SOURCE_WITH_SELFIE 'with_selfie';
  86.     const RESULT_SOURCE_TOP_100 'top_100';
  87.     const RESULT_SOURCE_ELITE 'elite';
  88.     const RESULT_SOURCE_MASSEURS 'masseurs';
  89.     const RESULT_SOURCE_MASSAGE_SERVICE 'massage_service';
  90.     const RESULT_SOURCE_BY_PARAMS 'by_params';
  91.     const RESULT_SOURCE_SERVICE 'service';
  92.     const RESULT_SOURCE_CITY 'city';
  93.     const RESULT_SOURCE_COUNTRY 'country';
  94.     const CACHE_ITEM_STATION_ADDED_PROFILES 'station_added_profiles_ids_';
  95.     private ?string $source null;
  96.     public function __construct(
  97.         private RequestStack $requestStack,
  98.         private ProfileList $profileList,
  99.         private CountryCurrencyResolver $countryCurrencyResolver,
  100.         private ServiceRepository $serviceRepository,
  101.         private ListingService $listingService,
  102.         private Features $features,
  103.         private ProfileFilterService $profilesFilterService,
  104.         private ProfileListSpecificationService $profileListSpecificationService,
  105.         private ProfileListingDataCreator $profileListingDataCreator,
  106.         private Top100ProfilesService $top100ProfilesService,
  107.         private CacheItemPoolInterface $stationAddedProfilesCache,
  108.         private ParameterBagInterface $parameterBag,
  109.         private ListingRotationApi $listingRotationApi,
  110.     ) {}
  111.     /**
  112.      * @Feature("has_masseurs")
  113.      */
  114.     #[ParamConverter("city"converter"city_converter")]
  115.     public function listForMasseur(City $cityServiceRepository $serviceRepository): Response
  116.     {
  117.         $specs $this->profileListSpecificationService->listForMasseur($city);
  118.         $response null;
  119.         try {
  120.             $result $this->listingRotationApi->paginate('/city/{city}/masseur', ['city' => $city->getId()], $this->getCurrentPageNumber());
  121.             $response = new Response();
  122.             $response->setMaxAge(10);
  123.         } catch (\Exception) {
  124.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  125.         }
  126.         $massageGroupServices $serviceRepository->findBy(['group' => ServiceGroups::MASSAGE]);
  127.         $orX $this->getORSpecForItemsArray([$massageGroupServices], function($item): ProfileIsProvidingOneOfServices {
  128.             return new ProfileIsProvidingOneOfServices($item);
  129.         });
  130.         $prevCount $result->count();
  131.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_MASSAGE_SERVICE);
  132.         if ($result->count() > $prevCount) {
  133.             $response?->setMaxAge(60);
  134.         }
  135.         return $this->render('ProfileList/list.html.twig', [
  136.             'profiles' => $result,
  137.             'source' => $this->source,
  138.             'source_default' => self::RESULT_SOURCE_MASSEURS,
  139.             'recommendationSpec' => $specs->recommendationSpec(),
  140.         ], response$response);
  141.     }
  142.     public function listByDefaultCity(ParameterBagInterface $parameterBagRequest $request): Response
  143.     {
  144.         $controller get_class($this).'::listByCity';
  145.         $path = [
  146.             'city' => $parameterBag->get('default_city'),
  147.             'subRequest' => true,
  148.         ];
  149.         //чтобы в обработчике можно было понять, по какому роуту зашли
  150.         $request->request->set('_route''profile_list.list_by_city');
  151.         return $this->forward($controller$path);
  152.     }
  153.     #[ParamConverter("city"converter"city_converter")]
  154.     public function listByCity(ParameterBagInterface $parameterBagRequest $requestCity $citybool $subRequest false): Response
  155.     {
  156.         $page $this->getCurrentPageNumber();
  157.         if ($this->features->redirect_default_city_to_homepage() && false === $subRequest && $city->equals($parameterBag->get('default_city')) && $page 2) {
  158.             return $this->redirectToRoute('homepage', [], 301);
  159.         }
  160.         $specs $this->profileListSpecificationService->listByCity();
  161.         $response null;
  162.         try {
  163.             $result $this->listingRotationApi->paginate('/city/{city}', ['city' => $city->getId()], $page);
  164.             $response = new Response();
  165.             $response->setMaxAge(10);
  166.         } catch (\Exception) {
  167.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  168.         }
  169.         return $this->render('ProfileList/list.html.twig', [
  170.             'profiles' => $result,
  171.             'recommendationSpec' => $specs->recommendationSpec(),
  172.         ], response$response);
  173.     }
  174.     #[ParamConverter("city"converter"city_converter")]
  175.     #[Entity("county"expr:"repository.ofUriIdentityWithinCity(county, city)")]
  176.     public function listByCounty(Request $requestCity $cityCounty $county): Response
  177.     {
  178.         if (!$city->hasCounty($county)) {
  179.             throw $this->createNotFoundException();
  180.         }
  181.         $specs $this->profileListSpecificationService->listByCounty($county);
  182.         $response null;
  183.         try {
  184.             $result $this->listingRotationApi->paginate('/city/{city}/county/{county}', ['city' => $city->getId(), 'county' => $county->getId()], $this->getCurrentPageNumber());
  185.             $response = new Response();
  186.             $response->setMaxAge(10);
  187.         } catch (\Exception) {
  188.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  189.         }
  190.         $prevCount $result->count();
  191.         $result $this->checkEmptyResultNotMasseur($result$citySpec::orX(ProfileIsLocated::withinCounties($city$city->getCounties()->toArray())), self::RESULT_SOURCE_COUNTY);
  192.         if ($result->count() > $prevCount) {
  193.             $response?->setMaxAge(60);
  194.         }
  195.         return $this->render('ProfileList/list.html.twig', [
  196.             'profiles' => $result,
  197.             'source' => $this->source,
  198.             'source_default' => self::RESULT_SOURCE_COUNTY,
  199.             'county' => $county,
  200.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  201.                 'city' => $city->getUriIdentity(),
  202.                 'county' => $county->getUriIdentity(),
  203.                 'page' => $this->getCurrentPageNumber()
  204.             ]),
  205.             'recommendationSpec' => $specs->recommendationSpec(),
  206.         ], response$response);
  207.     }
  208.     #[ParamConverter("city"converter"city_converter")]
  209.     #[Entity("district"expr:"repository.ofUriIdentityWithinCity(district, city)")]
  210.     public function listByDistrict(Request $requestCity $cityDistrict $district): Response
  211.     {
  212.         if (!$city->hasDistrict($district)) {
  213.             throw $this->createNotFoundException();
  214.         }
  215.         $specs $this->profileListSpecificationService->listByDistrict($district);
  216.         $response null;
  217.         try {
  218.             $result $this->listingRotationApi->paginate('/city/{city}/district/{district}', ['city' => $city->getId(), 'district' => $district->getId()], $this->getCurrentPageNumber());
  219.             $response = new Response();
  220.             $response->setMaxAge(10);
  221.         } catch (\Exception) {
  222.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  223.         }
  224.         $prevCount $result->count();
  225.         $result $this->checkEmptyResultNotMasseur($result$citySpec::orX(ProfileIsLocated::withinDistricts($city$city->getDistricts()->toArray())), self::RESULT_SOURCE_DISTRICT);
  226.         if ($result->count() > $prevCount) {
  227.             $response?->setMaxAge(60);
  228.         }
  229.         return $this->render('ProfileList/list.html.twig', [
  230.             'profiles' => $result,
  231.             'source' => $this->source,
  232.             'source_default' => self::RESULT_SOURCE_DISTRICT,
  233.             'district' => $district,
  234.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  235.                 'city' => $city->getUriIdentity(),
  236.                 'district' => $district->getUriIdentity(),
  237.                 'page' => $this->getCurrentPageNumber()
  238.             ]),
  239.             'recommendationSpec' => $specs->recommendationSpec(),
  240.         ], response$response);
  241.     }
  242.     #[ParamConverter("city"converter"city_converter")]
  243.     #[Entity("station"expr:"repository.ofUriIdentityWithinCity(station, city)")]
  244.     public function listByStation(Request $requestCity $cityStation $station): Response
  245.     {
  246.         if (!$city->hasStation($station)) {
  247.             throw $this->createNotFoundException();
  248.         }
  249.         $specs $this->profileListSpecificationService->listByStation($station);
  250.         $response null;
  251.         try {
  252.             $result $this->listingRotationApi->paginate('/city/{city}/station/{station}', ['city' => $city->getId(), 'station' => $station->getId()], $this->getCurrentPageNumber());
  253.             $response = new Response();
  254.             $response->setMaxAge(10);
  255.         } catch (\Exception) {
  256.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  257.         }
  258.         $prevCount $result->count();
  259.         if(true === $this->features->station_page_add_profiles()) {
  260.             $spread $this->parameterBag->get('app.profile.station_page.added_profiles.spread');
  261.             $result $this->addSinglePageStationResults($result$city$station$spread ?: 5);
  262.         }
  263.         if (null !== $station->getDistrict()) {
  264.             $result $this->checkEmptyResultNotMasseur($result$citySpec::orX(ProfileIsLocated::nearStations($city$station->getDistrict()->getStations()->toArray())), self::RESULT_SOURCE_STATION);
  265.         } else {
  266.             $result $this->checkCityAndCountrySource($result$city);
  267.         }
  268.         if ($result->count() > $prevCount) {
  269.             $response?->setMaxAge(60);
  270.         }
  271.         return $this->render('ProfileList/list.html.twig', [
  272.             'profiles' => $result,
  273.             'source' => $this->source,
  274.             'source_default' => self::RESULT_SOURCE_STATION,
  275.             'station' => $station,
  276.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  277.                 'city' => $city->getUriIdentity(),
  278.                 'station' => $station->getUriIdentity(),
  279.                 'page' => $this->getCurrentPageNumber()
  280.             ]),
  281.             'recommendationSpec' => $specs->recommendationSpec(),
  282.         ], response$response);
  283.     }
  284.     private function addSinglePageStationResults(Page $resultCity $cityStation $stationint $spread): Page
  285.     {
  286.         if($result->totalCount() >= $result->getCurrentLimit()) {
  287.             return $result;
  288.         }
  289.         $addedProfileIds $this->stationAddedProfilesCache->get(self::CACHE_ITEM_STATION_ADDED_PROFILES $station->getId(), function() use ($result$city$station$spread): array {
  290.             $currentSpread rand(0$spread);
  291.             $plannedTotalCount $result->getCurrentLimit() - $spread $currentSpread;
  292.             $result iterator_to_array($result->getIterator());
  293.             $originalProfileIds array_map(fn($item) => $item->id$result);
  294.             if($station->getDistrict()) {
  295.                 $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinDistrict($station->getDistrict())), $plannedTotalCount);
  296.             }
  297.             if($station->getDistrict()?->getCounty()) {
  298.                 $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinCounty($station->getDistrict()->getCounty())), $plannedTotalCount);
  299.             }
  300.             $result $this->addSinglePageResultsUptoAmount($result$citySpec::orX(ProfileIsLocated::withinCity($city)), $plannedTotalCount);
  301.             $result array_map(fn($item) => $item->id$result);
  302.             return array_diff($result$originalProfileIds);
  303.         });
  304.         $addedProfileIds array_slice($addedProfileIds0$result->getCurrentLimit() - $result->totalCount());
  305.         $originalProfiles iterator_to_array($result->getIterator());
  306.         $addedProfiles $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited($city, new ProfileIdIn($addedProfileIds), null, [Genders::FEMALE], count($addedProfileIds));
  307.         $newResult array_merge($originalProfiles$addedProfiles);
  308.         return new FakeORMQueryPage(01$result->getCurrentLimit(), count($newResult), $newResult);
  309.     }
  310.     private function addSinglePageResultsUptoAmount(array $resultCity $city, ?Filter $specsint $totalCount): array
  311.     {
  312.         $toAdd $totalCount count($result);
  313.         $currentResultIds array_map(fn($profile) => $profile->id$result);
  314.         $resultsToAdd $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited($city$specs, [new ProfileIdNotIn($currentResultIds)], [Genders::FEMALE], $toAdd);
  315.         $result array_merge($result$resultsToAdd);
  316.         return $result;
  317.     }
  318.     #[ParamConverter("city"converter"city_converter")]
  319.     public function listByStations(City $citystring $stationsStationRepository $stationRepository): Response
  320.     {
  321.         $stationIds explode(','$stations);
  322.         $stations $stationRepository->findBy(['uriIdentity' => $stationIds]);
  323.         $specs $this->profileListSpecificationService->listByStations($stations);
  324.         $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  325.         return $this->render('ProfileList/list.html.twig', [
  326.             'profiles' => $result,
  327.             'recommendationSpec' => $specs->recommendationSpec(),
  328.         ]);
  329.     }
  330.     #[ParamConverter("city"converter"city_converter")]
  331.     public function listApproved(Request $requestCity $city): Response
  332.     {
  333.         $specs $this->profileListSpecificationService->listApproved();
  334.         $response null;
  335.         try {
  336.             $result $this->listingRotationApi->paginate('/city/{city}/approved', ['city' => $city->getId()], $this->getCurrentPageNumber());
  337.             $response = new Response();
  338.             $response->setMaxAge(10);
  339.         } catch (\Exception) {
  340.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  341.         }
  342.         $prevCount $result->count();
  343.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  344.             $this->source self::RESULT_SOURCE_WITH_COMMENTS;
  345.             $result $this->listRandomSinglePage($citynull, new ProfileHasComments(), nulltruefalse);
  346.             if($result->count() == 0) {
  347.                 $this->source self::RESULT_SOURCE_WITH_VIDEO;
  348.                 $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  349.             }
  350.             if($result->count() == 0) {
  351.                 $this->source self::RESULT_SOURCE_ELITE;
  352.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  353.             }
  354.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  355.         }
  356.         if ($result->count() > $prevCount) {
  357.             $response?->setMaxAge(60);
  358.         }
  359.         return $this->render('ProfileList/list.html.twig', [
  360.             'profiles' => $result,
  361.             'source' => $this->source,
  362.             'source_default' => self::RESULT_SOURCE_APPROVED,
  363.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  364.                 'city' => $city->getUriIdentity(),
  365.                 'page' => $this->getCurrentPageNumber()
  366.             ]),
  367.             'recommendationSpec' => $specs->recommendationSpec(),
  368.         ], response$response);
  369.     }
  370.     #[ParamConverter("city"converter"city_converter")]
  371.     public function listWithComments(Request $requestCity $city): Response
  372.     {
  373.         $specs $this->profileListSpecificationService->listWithComments();
  374.         $response null;
  375.         try {
  376.             $result $this->listingRotationApi->paginate('/city/{city}/with_comments', ['city' => $city->getId()], $this->getCurrentPageNumber());
  377.             $response = new Response();
  378.             $response->setMaxAge(10);
  379.         } catch (\Exception) {
  380.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  381.         }
  382.         $prevCount $result->count();
  383.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  384.             $this->source self::RESULT_SOURCE_APPROVED;
  385.             $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  386.             if ($result->count() == 0) {
  387.                 $this->source self::RESULT_SOURCE_WITH_VIDEO;
  388.                 $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  389.             }
  390.             if ($result->count() == 0) {
  391.                 $this->source self::RESULT_SOURCE_ELITE;
  392.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  393.             }
  394.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  395.         }
  396.         if ($result->count() > $prevCount) {
  397.             $response?->setMaxAge(60);
  398.         }
  399.         return $this->render('ProfileList/list.html.twig', [
  400.             'profiles' => $result,
  401.             'source' => $this->source,
  402.             'source_default' => self::RESULT_SOURCE_WITH_COMMENTS,
  403.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  404.                 'city' => $city->getUriIdentity(),
  405.                 'page' => $this->getCurrentPageNumber()
  406.             ]),
  407.             'recommendationSpec' => $specs->recommendationSpec(),
  408.         ], response$response);
  409.     }
  410.     #[ParamConverter("city"converter"city_converter")]
  411.     public function listWithVideo(Request $requestCity $city): Response
  412.     {
  413.         $specs $this->profileListSpecificationService->listWithVideo();
  414.         $response null;
  415.         try {
  416.             $result $this->listingRotationApi->paginate('/city/{city}/with_video', ['city' => $city->getId()], $this->getCurrentPageNumber());
  417.             $response = new Response();
  418.             $response->setMaxAge(10);
  419.         } catch (\Exception) {
  420.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  421.         }
  422.         $prevCount $result->count();
  423.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  424.             $this->source self::RESULT_SOURCE_APPROVED;
  425.             $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  426.             if($result->count() == 0) {
  427.                 $this->source self::RESULT_SOURCE_WITH_COMMENTS;
  428.                 $result $this->listRandomSinglePage($citynull, new ProfileHasComments(), nulltruefalse);
  429.             }
  430.             if($result->count() == 0) {
  431.                 $this->source self::RESULT_SOURCE_ELITE;
  432.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  433.             }
  434.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  435.         }
  436.         if ($result->count() > $prevCount) {
  437.             $response?->setMaxAge(60);
  438.         }
  439.         return $this->render('ProfileList/list.html.twig', [
  440.             'profiles' => $result,
  441.             'source' => $this->source,
  442.             'source_default' => self::RESULT_SOURCE_WITH_VIDEO,
  443.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  444.                 'city' => $city->getUriIdentity(),
  445.                 'page' => $this->getCurrentPageNumber()
  446.             ]),
  447.             'recommendationSpec' => $specs->recommendationSpec(),
  448.         ], response$response);
  449.     }
  450.     #[ParamConverter("city"converter"city_converter")]
  451.     public function listWithSelfie(Request $requestCity $city): Response
  452.     {
  453.         $specs $this->profileListSpecificationService->listWithSelfie();
  454.         $response null;
  455.         try {
  456.             $result $this->listingRotationApi->paginate('/city/{city}/with_selfie', ['city' => $city->getId()], $this->getCurrentPageNumber());
  457.             $response = new Response();
  458.             $response->setMaxAge(10);
  459.         } catch (\Exception) {
  460.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  461.         }
  462.         $prevCount $result->count();
  463.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  464.             $this->source self::RESULT_SOURCE_WITH_VIDEO;
  465.             $result $this->listRandomSinglePage($citynull, new ProfileHasVideo(), nulltruefalse);
  466.             if ($result->count() == 0) {
  467.                 $this->source self::RESULT_SOURCE_APPROVED;
  468.                 $result $this->listRandomSinglePage($citynull, new ProfileIsApproved(), nulltruefalse);
  469.             }
  470.             if ($result->count() == 0) {
  471.                 $this->source self::RESULT_SOURCE_ELITE;
  472.                 $result $this->listRandomSinglePage($citynull$this->getSpecForEliteGirls($city), nulltruenull);
  473.             }
  474.             $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  475.         }
  476.         if ($result->count() > $prevCount) {
  477.             $response?->setMaxAge(60);
  478.         }
  479.         return $this->render('ProfileList/list.html.twig', [
  480.             'profiles' => $result,
  481.             'source' => $this->source,
  482.             'source_default' => self::RESULT_SOURCE_WITH_SELFIE,
  483.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  484.                 'city' => $city->getUriIdentity(),
  485.                 'page' => $this->getCurrentPageNumber()
  486.             ]),
  487.             'recommendationSpec' => $specs->recommendationSpec(),
  488.         ], response$response);
  489.     }
  490.     #[ParamConverter("city"converter"city_converter")]
  491.     #[Feature("extra_category_top_100")]
  492.     public function listTop100(Request $requestCity $city): Response
  493.     {
  494.         $specs $this->profileListSpecificationService->listApproved();
  495.         $topIds $this->top100ProfilesService->getProfileIds($city);
  496.         if (empty($topIds)) {
  497.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement(
  498.                 $city,
  499.                 new ProfileIsApproved(),
  500.             );
  501.         } else {
  502.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement(
  503.                 $city,
  504.                 new ProfileIdINOrderedByINValues($topIds),
  505.                 [new ProfileIsApproved()],
  506.             );
  507.         }
  508.         return $this->render('ProfileList/list.html.twig', [
  509.             'profiles' => $result,
  510.             'source' => self::RESULT_SOURCE_TOP_100,
  511.             'source_default' => self::RESULT_SOURCE_TOP_100,
  512.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  513.                 'city' => $city->getUriIdentity(),
  514.                 'page' => $this->getCurrentPageNumber(),
  515.             ]),
  516.             'recommendationSpec' => $specs->recommendationSpec(),
  517.         ]);
  518.     }
  519.     #[ParamConverter("city"converter"city_converter")]
  520.     public function listByPrice(Request $requestCountryCurrencyResolver $countryCurrencyResolverCity $citystring $priceTypeint $minPrice nullint $maxPrice null): Response
  521.     {
  522.         $specs $this->profileListSpecificationService->listByPrice($city$priceType$minPrice$maxPrice);
  523.         $response null;
  524.         try {
  525.             if (!in_array($priceType, ['low''high''elite'])) {
  526.                 throw new \LogicException(sprintf('Price type "%s" is not supported'$priceType));
  527.             }
  528.             $result $this->listingRotationApi->paginate('/city/{city}/price/'.$priceType, ['city' => $city->getId()], $this->getCurrentPageNumber());
  529.             $response = new Response();
  530.             $response->setMaxAge(10);
  531.         } catch (\Exception) {
  532.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  533.         }
  534.         $prevCount $result->count();
  535.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  536.             $result $this->processListByPriceEmptyResult($result$city$priceType$minPrice$maxPrice);
  537.         }
  538.         if ($result->count() > $prevCount) {
  539.             $response?->setMaxAge(60);
  540.         }
  541.         return $this->render('ProfileList/list.html.twig', [
  542.             'profiles' => $result,
  543.             'source' => $this->source,
  544.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  545.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  546.                 'city' => $city->getUriIdentity(),
  547.                 'priceType' => $priceType,
  548.                 'minPrice' => $minPrice,
  549.                 'maxPrice' => $maxPrice,
  550.                 'page' => $this->getCurrentPageNumber()
  551.             ]),
  552.             'recommendationSpec' => $specs->recommendationSpec(),
  553.         ], response$response);
  554.     }
  555.     private function processListByPriceEmptyResult(Page $resultCity $citystring $priceTypeint $minPrice nullint $maxPrice null)
  556.     {
  557.         if(!$this->features->fill_empty_profile_list())
  558.             return $result;
  559.         $this->source self::RESULT_SOURCE_BY_PARAMS;
  560.         if($this->countryCurrencyResolver->getCurrencyFor($city->getCountryCode()) == 'RUB') {
  561.             if ($minPrice && $maxPrice) {
  562.                 if ($minPrice == 2000 && $maxPrice == 3000) {
  563.                     $priceSpec = [
  564.                         ProfileWithApartmentsOneHourPrice::range(15002000),
  565.                         ProfileWithApartmentsOneHourPrice::range(30004000),
  566.                     ];
  567.                 } else if ($minPrice == 3000 && $maxPrice == 4000) {
  568.                     $priceSpec = [
  569.                         ProfileWithApartmentsOneHourPrice::range(20003000),
  570.                         ProfileWithApartmentsOneHourPrice::range(40005000),
  571.                     ];
  572.                 } else if ($minPrice == 4000 && $maxPrice == 5000) {
  573.                     $priceSpec = [
  574.                         ProfileWithApartmentsOneHourPrice::range(30004000),
  575.                         ProfileWithApartmentsOneHourPrice::range(50006000),
  576.                     ];
  577.                 } else if ($minPrice == 5000 && $maxPrice == 6000) {
  578.                     $priceSpec = [
  579.                         ProfileWithApartmentsOneHourPrice::range(4000999999)
  580.                     ];
  581.                 } else {
  582.                     $priceSpec = [
  583.                         ProfileWithApartmentsOneHourPrice::range($minPrice$maxPrice)
  584.                     ];
  585.                 }
  586.                 $result $this->listRandomSinglePage($citynullnull$priceSpectruefalse);
  587.             } elseif ($maxPrice) {
  588.                 if ($maxPrice == 500) {
  589.                     $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(1500);
  590.                     $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  591.                     if ($result->count() == 0) {
  592.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(15002000);
  593.                         $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  594.                     }
  595.                 } else if ($maxPrice == 1500) {
  596.                     $priceSpec ProfileWithApartmentsOneHourPrice::range(15002000);
  597.                     $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  598.                     if ($result->count() == 0) {
  599.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(20003000);
  600.                         $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  601.                     }
  602.                 }
  603.             } else {
  604.                 switch ($priceType) {
  605.                     case 'not_expensive':
  606.                         $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(2000);
  607.                         break;
  608.                     case 'high':
  609.                         $priceSpec ProfileWithApartmentsOneHourPrice::range(30006000);
  610.                         break;
  611.                     case 'low':
  612.                         $priceSpec ProfileWithApartmentsOneHourPrice::cheaperThan(2000);
  613.                         break;
  614.                     case 'elite':
  615.                         $priceSpec ProfileWithApartmentsOneHourPrice::moreExpensiveThan(6000);
  616.                         break;
  617.                     default:
  618.                         throw new \LogicException('Unknown price type');
  619.                         break;
  620.                 }
  621.                 $result $this->listRandomSinglePage($citynull$priceSpecnulltruefalse);
  622.             }
  623.         }
  624.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  625.         return $result;
  626.     }
  627.     #[ParamConverter("city"converter"city_converter")]
  628.     public function listByAge(Request $requestCity $citystring $ageTypeint $minAge nullint $maxAge null): Response
  629.     {
  630.         $specs $this->profileListSpecificationService->listByAge($ageType$minAge$maxAge);
  631.         $response null;
  632.         try {
  633.             if (!in_array($ageType, ['young''old'])) {
  634.                 throw new \LogicException(sprintf('Age type "%s" is not supported'$ageType));
  635.             }
  636.             $result $this->listingRotationApi->paginate('/city/{city}/age/'.$ageType, ['city' => $city->getId()], $this->getCurrentPageNumber());
  637.             $response = new Response();
  638.             $response->setMaxAge(10);
  639.         } catch (\Exception) {
  640.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  641.         }
  642.         $prevCount $result->count();
  643.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  644.             $filled $this->processListByAgeEmptyResult($result$city$ageType$minAge$maxAge);
  645.             if($filled)
  646.                 $result $filled;
  647.         }
  648.         if ($result->count() > $prevCount) {
  649.             $response?->setMaxAge(60);
  650.         }
  651.         return $this->render('ProfileList/list.html.twig', [
  652.             'profiles' => $result,
  653.             'source' => $this->source,
  654.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  655.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  656.                 'city' => $city->getUriIdentity(),
  657.                 'ageType' => $ageType,
  658.                 'minAge' => $minAge,
  659.                 'maxAge' => $maxAge,
  660.                 'page' => $this->getCurrentPageNumber()
  661.             ]),
  662.             'recommendationSpec' => $specs->recommendationSpec(),
  663.         ], response$response);
  664.     }
  665.     private function processListByAgeEmptyResult(Page $resultCity $citystring $ageTypeint $minAge nullint $maxAge null)
  666.     {
  667.         if(!$this->features->fill_empty_profile_list())
  668.             return $result;
  669.         $this->source self::RESULT_SOURCE_BY_PARAMS;
  670.         if ($minAge && !$maxAge) {
  671.             $startMinAge $minAge;
  672.             do {
  673.                 $startMinAge -= 2;
  674.                 $ageSpec ProfileWithAge::olderThan($startMinAge);
  675.                 $result $this->listRandomSinglePage($citynull$ageSpecnulltruefalse);
  676.             } while($result->count() == && $startMinAge >= 18);
  677.         } else if($ageType == 'young') {
  678.             $startMaxAge 20;
  679.             do {
  680.                 $startMaxAge += 2;
  681.                 $ageSpec ProfileWithAge::youngerThan($startMaxAge);
  682.                 $result $this->listRandomSinglePage($citynull$ageSpecnulltruefalse);
  683.             } while($result->count() == && $startMaxAge <= 100);
  684.         }
  685.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  686.         return $result;
  687.     }
  688.     #[ParamConverter("city"converter"city_converter")]
  689.     public function listByHeight(Request $requestCity $citystring $heightType): Response
  690.     {
  691.         $specs $this->profileListSpecificationService->listByHeight($heightType);
  692.         $response null;
  693.         try {
  694.             $result $this->listingRotationApi->paginate('/city/{city}/height/'.$heightType, ['city' => $city->getId()], $this->getCurrentPageNumber());
  695.             $response = new Response();
  696.             $response->setMaxAge(10);
  697.         } catch (\Exception) {
  698.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  699.         }
  700.         $prevCount $result->count();
  701.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  702.         if ($result->count() > $prevCount) {
  703.             $response?->setMaxAge(60);
  704.         }
  705.         return $this->render('ProfileList/list.html.twig', [
  706.             'profiles' => $result,
  707.             'source' => $this->source,
  708.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  709.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  710.                 'city' => $city->getUriIdentity(),
  711.                 'heightType' => $heightType,
  712.                 'page' => $this->getCurrentPageNumber()
  713.             ]),
  714.             'recommendationSpec' => $specs->recommendationSpec(),
  715.         ], response$response);
  716.     }
  717.     #[ParamConverter("city"converter"city_converter")]
  718.     public function listByBreastType(Request $requestCity $citystring $breastType): Response
  719.     {
  720.         if(null === $type BreastTypes::getValueByUriIdentity($breastType))
  721.             throw $this->createNotFoundException();
  722.         $specs $this->profileListSpecificationService->listByBreastType($breastType);
  723.         $response null;
  724.         try {
  725.             $result $this->listingRotationApi->paginate('/city/{city}/breasttype/'.$type, ['city' => $city->getId()], $this->getCurrentPageNumber());
  726.             $response = new Response();
  727.             $response->setMaxAge(10);
  728.         } catch (\Exception) {
  729.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  730.         }
  731.         $orX $this->getORSpecForItemsArray(BreastTypes::getList(), function($item): ProfileWithBreastType {
  732.             return new ProfileWithBreastType($item);
  733.         });
  734.         $prevCount $result->count();
  735.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  736.         if ($result->count() > $prevCount) {
  737.             $response?->setMaxAge(60);
  738.         }
  739.         return $this->render('ProfileList/list.html.twig', [
  740.             'profiles' => $result,
  741.             'source' => $this->source,
  742.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  743.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  744.                 'city' => $city->getUriIdentity(),
  745.                 'breastType' => $breastType,
  746.                 'page' => $this->getCurrentPageNumber()
  747.             ]),
  748.             'recommendationSpec' => $specs->recommendationSpec(),
  749.         ], response$response);
  750.     }
  751.     #[ParamConverter("city"converter"city_converter")]
  752.     public function listByHairColor(Request $requestCity $citystring $hairColor): Response
  753.     {
  754.         if(null === $color HairColors::getValueByUriIdentity($hairColor))
  755.             throw $this->createNotFoundException();
  756.         $specs $this->profileListSpecificationService->listByHairColor($hairColor);
  757.         $response null;
  758.         try {
  759.             $result $this->listingRotationApi->paginate('/city/{city}/haircolor/'.$color, ['city' => $city->getId()], $this->getCurrentPageNumber());
  760.             $response = new Response();
  761.             $response->setMaxAge(10);
  762.         } catch (\Exception) {
  763.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  764.         }
  765.         $orX $this->getORSpecForItemsArray(HairColors::getList(), function($item): ProfileWithHairColor {
  766.             return new ProfileWithHairColor($item);
  767.         });
  768.         $prevCount $result->count();
  769.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  770.         if ($result->count() > $prevCount) {
  771.             $response?->setMaxAge(60);
  772.         }
  773.         return $this->render('ProfileList/list.html.twig', [
  774.             'profiles' => $result,
  775.             'source' => $this->source,
  776.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  777.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  778.                 'city' => $city->getUriIdentity(),
  779.                 'hairColor' => $hairColor,
  780.                 'page' => $this->getCurrentPageNumber()
  781.             ]),
  782.             'recommendationSpec' => $specs->recommendationSpec(),
  783.         ], response$response);
  784.     }
  785.     #[ParamConverter("city"converter"city_converter")]
  786.     public function listByBodyType(Request $requestCity $citystring $bodyType): Response
  787.     {
  788.         if(null === $type BodyTypes::getValueByUriIdentity($bodyType))
  789.             throw $this->createNotFoundException();
  790.         $specs $this->profileListSpecificationService->listByBodyType($bodyType);
  791.         $response null;
  792.         try {
  793.             $result $this->listingRotationApi->paginate('/city/{city}/bodytype/'.$type, ['city' => $city->getId()], $this->getCurrentPageNumber());
  794.             $response = new Response();
  795.             $response->setMaxAge(10);
  796.         } catch (\Exception) {
  797.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  798.         }
  799.         $orX $this->getORSpecForItemsArray(BodyTypes::getList(), function($item): ProfileWithBodyType {
  800.             return new ProfileWithBodyType($item);
  801.         });
  802.         $prevCount $result->count();
  803.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  804.         if ($result->count() > $prevCount) {
  805.             $response?->setMaxAge(60);
  806.         }
  807.         return $this->render('ProfileList/list.html.twig', [
  808.             'profiles' => $result,
  809.             'source' => $this->source,
  810.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  811.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  812.                 'city' => $city->getUriIdentity(),
  813.                 'bodyType' => $bodyType,
  814.                 'page' => $this->getCurrentPageNumber()
  815.             ]),
  816.             'recommendationSpec' => $specs->recommendationSpec(),
  817.         ], response$response);
  818.     }
  819.     #[ParamConverter("city"converter"city_converter")]
  820.     public function listByPlace(Request $requestCity $citystring $placeTypestring $takeOutLocation null): Response
  821.     {
  822.         $specs $this->profileListSpecificationService->listByPlace($placeType$takeOutLocation);
  823.         if(null === $specs)
  824.             throw $this->createNotFoundException();
  825.         $response null;
  826.         try {
  827.             $endpoint '/city/{city}/place/'.$placeType;
  828.             if (null !== $takeOutLocation) {
  829.                 $endpoint .= '/'.$takeOutLocation;
  830.             }
  831.             $result $this->listingRotationApi->paginate($endpoint, ['city' => $city->getId()], $this->getCurrentPageNumber());
  832.             $response = new Response();
  833.             $response->setMaxAge(10);
  834.         } catch (\Exception) {
  835.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  836.         }
  837.         $orX $this->getORSpecForItemsArray(TakeOutLocations::getList(), function($item): ProfileIsProvidingTakeOut {
  838.             return new ProfileIsProvidingTakeOut($item);
  839.         });
  840.         if($placeType == 'take-out')
  841.             $orX->orX(new ProfileHasApartments());
  842.         $prevCount $result->count();
  843.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  844.         if ($result->count() > $prevCount) {
  845.             $response?->setMaxAge(60);
  846.         }
  847.         return $this->render('ProfileList/list.html.twig', [
  848.             'profiles' => $result,
  849.             'source' => $this->source,
  850.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  851.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  852.                 'city' => $city->getUriIdentity(),
  853.                 'placeType' => $placeType,
  854.                 'takeOutLocation' => TakeOutLocations::getUriIdentity(TakeOutLocations::getValueByUriIdentity($takeOutLocation)),
  855.                 'page' => $this->getCurrentPageNumber()
  856.             ]),
  857.             'recommendationSpec' => $specs->recommendationSpec(),
  858.         ], response$response);
  859.     }
  860.     #[ParamConverter("city"converter"city_converter")]
  861.     public function listByPrivateHaircut(Request $requestCity $citystring $privateHaircut): Response
  862.     {
  863.         if(null === $type PrivateHaircuts::getValueByUriIdentity($privateHaircut))
  864.             throw $this->createNotFoundException();
  865.         $specs $this->profileListSpecificationService->listByPrivateHaircut($privateHaircut);
  866.         $response null;
  867.         try {
  868.             $result $this->listingRotationApi->paginate('/city/{city}/privatehaircut/'.$type, ['city' => $city->getId()], $this->getCurrentPageNumber());
  869.             $response = new Response();
  870.             $response->setMaxAge(10);
  871.         } catch (\Exception) {
  872.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  873.         }
  874.         $orX $this->getORSpecForItemsArray(PrivateHaircuts::getList(), function($item): ProfileWithPrivateHaircut {
  875.             return new ProfileWithPrivateHaircut($item);
  876.         });
  877.         $prevCount $result->count();
  878.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  879.         if ($result->count() > $prevCount) {
  880.             $response?->setMaxAge(60);
  881.         }
  882.         return $this->render('ProfileList/list.html.twig', [
  883.             'profiles' => $result,
  884.             'source' => $this->source,
  885.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  886.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  887.                 'city' => $city->getUriIdentity(),
  888.                 'privateHaircut' => $privateHaircut,
  889.                 'page' => $this->getCurrentPageNumber()
  890.             ]),
  891.             'recommendationSpec' => $specs->recommendationSpec(),
  892.         ], response$response);
  893.     }
  894.     #[ParamConverter("city"converter"city_converter")]
  895.     public function listByNationality(Request $requestCity $citystring $nationality): Response
  896.     {
  897.         if(null === $type Nationalities::getValueByUriIdentity($nationality))
  898.             throw $this->createNotFoundException();
  899.         $specs $this->profileListSpecificationService->listByNationality($nationality);
  900.         $response null;
  901.         try {
  902.             $result $this->listingRotationApi->paginate('/city/{city}/nationality/'.$type, ['city' => $city->getId()], $this->getCurrentPageNumber());
  903.             $response = new Response();
  904.             $response->setMaxAge(10);
  905.         } catch (\Exception) {
  906.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  907.         }
  908.         $orX $this->getORSpecForItemsArray(Nationalities::getList(), function($item): ProfileWithNationality {
  909.             return new ProfileWithNationality($item);
  910.         });
  911.         $prevCount $result->count();
  912.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_BY_PARAMS);
  913.         if ($result->count() > $prevCount) {
  914.             $response?->setMaxAge(60);
  915.         }
  916.         return $this->render('ProfileList/list.html.twig', [
  917.             'profiles' => $result,
  918.             'source' => $this->source,
  919.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  920.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  921.                 'city' => $city->getUriIdentity(),
  922.                 'nationality' => $nationality,
  923.                 'page' => $this->getCurrentPageNumber()
  924.             ]),
  925.             'recommendationSpec' => $specs->recommendationSpec(),
  926.         ], response$response);
  927.     }
  928.     #[ParamConverter("city"converter"city_converter")]
  929.     #[ParamConverter("service"options: ['mapping' => ['service' => 'uriIdentity']])]
  930.     public function listByProvidedService(Request $requestCity $cityService $service): Response
  931.     {
  932.         $specs $this->profileListSpecificationService->listByProvidedService($service$city);
  933.         $response null;
  934.         try {
  935.             $result $this->listingRotationApi->paginate('/city/{city}/service/{service}', ['city' => $city->getId(), 'service' => $service->getId()], $this->getCurrentPageNumber());
  936.             $response = new Response();
  937.             $response->setMaxAge(10);
  938.         } catch (\Exception) {
  939.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  940.         }
  941.         $prevCount $result->count();
  942.         $sameGroupServices $this->serviceRepository->findBy(['group' => $service->getGroup()]);
  943.         $orX $this->getORSpecForItemsArray([$sameGroupServices], function($item): ProfileIsProvidingOneOfServices {
  944.             return new ProfileIsProvidingOneOfServices($item);
  945.         });
  946.         $result $this->checkEmptyResultNotMasseur($result$city$orXself::RESULT_SOURCE_SERVICE);
  947.         if ($result->count() > $prevCount) {
  948.             $response?->setMaxAge(60);
  949.         }
  950.         return $this->render('ProfileList/list.html.twig', [
  951.             'profiles' => $result,
  952.             'source' => $this->source,
  953.             'source_default' => self::RESULT_SOURCE_SERVICE,
  954.             'service' => $service,
  955.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  956.                 'city' => $city->getUriIdentity(),
  957.                 'service' => $service->getUriIdentity(),
  958.                 'page' => $this->getCurrentPageNumber()
  959.             ]),
  960.             'recommendationSpec' => $specs->recommendationSpec(),
  961.         ], response$response);
  962.     }
  963.     /**
  964.      * @Feature("has_archive_page")
  965.      */
  966.     #[ParamConverter("city"converter"city_converter")]
  967.     public function listArchived(Request $requestCity $city): Response
  968.     {
  969.         $result $this->profileList->list($citynullnullnullfalsenullProfileList::ORDER_BY_UPDATED);
  970.         return $this->render('ProfileList/list.html.twig', [
  971.             'profiles' => $result,
  972.             'recommendationSpec' => new \App\Specification\ElasticSearch\ProfileIsNotArchived(), //ProfileIsArchived, согласно https://redminez.net/issues/28305 в реках выводятся неарзивные
  973.         ]);
  974.     }
  975.     #[ParamConverter("city"converter"city_converter")]
  976.     public function listNew(City $cityint $weeks 2): Response
  977.     {
  978.         $specs $this->profileListSpecificationService->listNew($weeks);
  979.         $response null;
  980.         try {
  981.             $result $this->listingRotationApi->paginate('/city/{city}/recent', ['city' => $city->getId()], $this->getCurrentPageNumber());
  982.             $response = new Response();
  983.             $response->setMaxAge(10);
  984.         } catch (\Exception) {
  985.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  986.         }
  987.         return $this->render('ProfileList/list.html.twig', [
  988.             'profiles' => $result,
  989.             'recommendationSpec' => $specs->recommendationSpec(),
  990.         ], response$response);
  991.     }
  992.     #[ParamConverter("city"converter"city_converter")]
  993.     public function listByNoRetouch(City $city): Response
  994.     {
  995.         $specs $this->profileListSpecificationService->listByNoRetouch();
  996.         $response null;
  997.         try {
  998.             $result $this->listingRotationApi->paginate('/city/{city}/noretouch', ['city' => $city->getId()], $this->getCurrentPageNumber());
  999.             $response = new Response();
  1000.             $response->setMaxAge(10);
  1001.         } catch (\Exception) {
  1002.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1003.         }
  1004.         $prevCount $result->count();
  1005.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1006.         if ($result->count() > $prevCount) {
  1007.             $response?->setMaxAge(60);
  1008.         }
  1009.         return $this->render('ProfileList/list.html.twig', [
  1010.             'profiles' => $result,
  1011.             'source' => $this->source,
  1012.             'recommendationSpec' => $specs->recommendationSpec(),
  1013.         ], response$response);
  1014.     }
  1015.     #[ParamConverter("city"converter"city_converter")]
  1016.     public function listByNice(City $city): Response
  1017.     {
  1018.         $specs $this->profileListSpecificationService->listByNice();
  1019.         $response null;
  1020.         try {
  1021.             $result $this->listingRotationApi->paginate('/city/{city}/nice', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1022.             $response = new Response();
  1023.             $response->setMaxAge(10);
  1024.         } catch (\Exception) {
  1025.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1026.         }
  1027.         $prevCount $result->count();
  1028.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1029.         if ($result->count() > $prevCount) {
  1030.             $response?->setMaxAge(60);
  1031.         }
  1032.         return $this->render('ProfileList/list.html.twig', [
  1033.             'profiles' => $result,
  1034.             'source' => $this->source,
  1035.             'recommendationSpec' => $specs->recommendationSpec(),
  1036.         ], response$response);
  1037.     }
  1038.     #[ParamConverter("city"converter"city_converter")]
  1039.     public function listByOnCall(City $city): Response
  1040.     {
  1041.         $specs $this->profileListSpecificationService->listByOnCall();
  1042.         $response null;
  1043.         try {
  1044.             $result $this->listingRotationApi->paginate('/city/{city}/oncall', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1045.             $response = new Response();
  1046.             $response->setMaxAge(10);
  1047.         } catch (\Exception) {
  1048.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1049.         }
  1050.         $prevCount $result->count();
  1051.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1052.         if ($result->count() > $prevCount) {
  1053.             $response?->setMaxAge(60);
  1054.         }
  1055.         return $this->render('ProfileList/list.html.twig', [
  1056.             'profiles' => $result,
  1057.             'source' => $this->source,
  1058.             'recommendationSpec' => $specs->recommendationSpec(),
  1059.         ], response$response);
  1060.     }
  1061.     #[ParamConverter("city"converter"city_converter")]
  1062.     public function listForHour(City $city): Response
  1063.     {
  1064.         $specs $this->profileListSpecificationService->listForHour();
  1065.         $response null;
  1066.         try {
  1067.             $result $this->listingRotationApi->paginate('/city/{city}/forhour', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1068.             $response = new Response();
  1069.             $response->setMaxAge(10);
  1070.         } catch (\Exception) {
  1071.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1072.         }
  1073.         $prevCount $result->count();
  1074.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1075.         if ($result->count() > $prevCount) {
  1076.             $response?->setMaxAge(60);
  1077.         }
  1078.         return $this->render('ProfileList/list.html.twig', [
  1079.             'profiles' => $result,
  1080.             'source' => $this->source,
  1081.             'recommendationSpec' => $specs->recommendationSpec(),
  1082.         ], response$response);
  1083.     }
  1084.     #[ParamConverter("city"converter"city_converter")]
  1085.     public function listForNight(City $city): Response
  1086.     {
  1087.         $specs $this->profileListSpecificationService->listForNight();
  1088.         $response null;
  1089.         try {
  1090.             $result $this->listingRotationApi->paginate('/city/{city}/fornight', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1091.             $response = new Response();
  1092.             $response->setMaxAge(10);
  1093.         } catch (\Exception) {
  1094.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1095.         }
  1096.         $prevCount $result->count();
  1097.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1098.         if ($result->count() > $prevCount) {
  1099.             $response?->setMaxAge(60);
  1100.         }
  1101.         return $this->render('ProfileList/list.html.twig', [
  1102.             'profiles' => $result,
  1103.             'source' => $this->source,
  1104.             'recommendationSpec' => $specs->recommendationSpec(),
  1105.         ], response$response);
  1106.     }
  1107.     private function getSpecForEliteGirls(City $city):Filter
  1108.     {
  1109.         $minPrice $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  1110.             'RUB' => 5000,
  1111.             'UAH' => 1500,
  1112.             'USD' => 100,
  1113.             'EUR' => 130,
  1114.         ]);
  1115.         return new ProfileIsElite($minPrice);
  1116.     }
  1117.     private function getElasticSearchSpecForEliteGirls(City $city): ISpecification
  1118.     {
  1119.         $minPrice $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  1120.             'RUB' => 5000,
  1121.             'UAH' => 1500,
  1122.             'USD' => 100,
  1123.             'EUR' => 130,
  1124.         ]);
  1125.         return new \App\Specification\ElasticSearch\ProfileIsElite($minPrice);
  1126.     }
  1127.     #[ParamConverter("city"converter"city_converter")]
  1128.     public function listForEliteGirls(CountryCurrencyResolver $countryCurrencyResolverRequest $requestCity $city): Response
  1129.     {
  1130.         $specs $this->profileListSpecificationService->listForEliteGirls($city);
  1131.         $response null;
  1132.         try {
  1133.             $result $this->listingRotationApi->paginate('/city/{city}/elite', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1134.             $response = new Response();
  1135.             $response->setMaxAge(10);
  1136.         } catch (\Exception) {
  1137.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1138.         }
  1139.         $prevCount $result->count();
  1140.         if($this->features->fill_empty_profile_list() && $result->count() == 0) {
  1141.             $prices = [
  1142.                 'RUB' => 5000,
  1143.                 'UAH' => 1500,
  1144.                 'USD' => 100,
  1145.                 'EUR' => 130,
  1146.             ];
  1147.             $currency $countryCurrencyResolver->getCurrencyFor($city->getCountryCode());
  1148.             if(isset($prices[$currency])) {
  1149.                 $minPrice $prices[$currency];
  1150.                 switch ($currency) {
  1151.                     case 'RUB'$diff 1000; break;
  1152.                     case 'UAH'$diff 500; break;
  1153.                     case 'USD':
  1154.                     case 'EUR'$diff 20; break;
  1155.                     default:
  1156.                         throw new \LogicException('Unexpected currency code');
  1157.                 }
  1158.                 while ($minPrice >= $diff) {
  1159.                     $minPrice -= $diff;
  1160.                     $result $this->listRandomSinglePage($citynullProfileWithApartmentsOneHourPrice::moreExpensiveThan($minPrice), nulltruefalse);
  1161.                     if ($result->count() > 0) {
  1162.                         $this->source self::RESULT_SOURCE_BY_PARAMS;
  1163.                         break;
  1164.                     }
  1165.                 }
  1166.                 $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1167.             }
  1168.         }
  1169.         if ($result->count() > $prevCount) {
  1170.             $response?->setMaxAge(60);
  1171.         }
  1172.         return $this->render('ProfileList/list.html.twig', [
  1173.             'profiles' => $result,
  1174.             'source' => $this->source,
  1175.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  1176.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1177.                 'city' => $city->getUriIdentity(),
  1178.                 'page' => $this->getCurrentPageNumber()
  1179.             ]),
  1180.             'recommendationSpec' => $specs->recommendationSpec(),
  1181.         ], response$response);
  1182.     }
  1183.     #[ParamConverter("city"converter"city_converter")]
  1184.     public function listForRealElite(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1185.     {
  1186.         $specs $this->profileListSpecificationService->listForRealElite($city);
  1187.         $response null;
  1188.         try {
  1189.             $result $this->listingRotationApi->paginate('/city/{city}/realelite', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1190.             $response = new Response();
  1191.             $response->setMaxAge(10);
  1192.         } catch (\Exception) {
  1193.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1194.         }
  1195.         $prevCount $result->count();
  1196.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1197.         if ($result->count() > $prevCount) {
  1198.             $response?->setMaxAge(60);
  1199.         }
  1200.         return $this->render('ProfileList/list.html.twig', [
  1201.             'profiles' => $result,
  1202.             'source' => $this->source,
  1203.             'recommendationSpec' => $specs->recommendationSpec(),
  1204.         ], response$response);
  1205.     }
  1206.     #[ParamConverter("city"converter"city_converter")]
  1207.     public function listForVipPros(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1208.     {
  1209.         $specs $this->profileListSpecificationService->listForVipPros($city);
  1210.         $response null;
  1211.         try {
  1212.             $result $this->listingRotationApi->paginate('/city/{city}/vip', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1213.             $response = new Response();
  1214.             $response->setMaxAge(10);
  1215.         } catch (\Exception) {
  1216.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1217.         }
  1218.         $prevCount $result->count();
  1219.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1220.         if ($result->count() > $prevCount) {
  1221.             $response?->setMaxAge(60);
  1222.         }
  1223.         return $this->render('ProfileList/list.html.twig', [
  1224.             'profiles' => $result,
  1225.             'source' => $this->source,
  1226.             'recommendationSpec' => $specs->recommendationSpec(),
  1227.         ], response$response);
  1228.     }
  1229.     #[ParamConverter("city"converter"city_converter")]
  1230.     public function listForVipIndividual(CountryCurrencyResolver $countryCurrencyResolverCity $city): Response
  1231.     {
  1232.         $specs $this->profileListSpecificationService->listForVipIndividual($city);
  1233.         $response null;
  1234.         try {
  1235.             $result $this->listingRotationApi->paginate('/city/{city}/vipindi', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1236.             $response = new Response();
  1237.             $response->setMaxAge(10);
  1238.         } catch (\Exception) {
  1239.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1240.         }
  1241.         $prevCount $result->count();
  1242.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1243.         if ($result->count() > $prevCount) {
  1244.             $response?->setMaxAge(60);
  1245.         }
  1246.         return $this->render('ProfileList/list.html.twig', [
  1247.             'profiles' => $result,
  1248.             'source' => $this->source,
  1249.             'recommendationSpec' => $specs->recommendationSpec(),
  1250.         ], response$response);
  1251.     }
  1252.     #[ParamConverter("city"converter"city_converter")]
  1253.     public function listForVipGirlsCity(City $city): Response
  1254.     {
  1255.         $specs $this->profileListSpecificationService->listForVipGirlsCity($city);
  1256.         $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1257.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1258.         return $this->render('ProfileList/list.html.twig', [
  1259.             'profiles' => $result,
  1260.             'source' => $this->source,
  1261.             'recommendationSpec' => $specs->recommendationSpec(),
  1262.         ]);
  1263.     }
  1264.     #[ParamConverter("city"converter"city_converter")]
  1265.     public function listOfGirlfriends(City $city): Response
  1266.     {
  1267.         $specs $this->profileListSpecificationService->listOfGirlfriends();
  1268.         $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1269.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1270.         return $this->render('ProfileList/list.html.twig', [
  1271.             'profiles' => $result,
  1272.             'source' => $this->source,
  1273.             'recommendationSpec' => $specs->recommendationSpec(),
  1274.         ]);
  1275.     }
  1276.     #[ParamConverter("city"converter"city_converter")]
  1277.     public function listOfMostExpensive(City $city): Response
  1278.     {
  1279.         $specs $this->profileListSpecificationService->listOfMostExpensive($city);
  1280.         $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec());
  1281.         $result $this->checkEmptyResultNotMasseur($result$citynullself::RESULT_SOURCE_CITY);
  1282.         return $this->render('ProfileList/list.html.twig', [
  1283.             'profiles' => $result,
  1284.             'source' => $this->source,
  1285.             'recommendationSpec' => $specs->recommendationSpec(),
  1286.         ]);
  1287.     }
  1288.     #[ParamConverter("city"converter"city_converter")]
  1289.     public function listBdsm(City $cityServiceRepository $serviceRepositoryParameterBagInterface $parameterBag): Response
  1290.     {
  1291.         $specs $this->profileListSpecificationService->listBdsm();
  1292.         $response null;
  1293.         try {
  1294.             $result $this->listingRotationApi->paginate('/city/{city}/bdsm', ['city' => $city->getId()], $this->getCurrentPageNumber());
  1295.             $response = new Response();
  1296.             $response->setMaxAge(10);
  1297.         } catch (\Exception) {
  1298.             $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec(), $specs->additionalSpecs());
  1299.         }
  1300.         $bdsmIds $serviceRepository->findBy(['group' => ServiceGroups::BDSM]);
  1301.         return $this->render('ProfileList/list.html.twig', [
  1302.             'profiles' => $result,
  1303.             'recommendationSpec' => $specs->recommendationSpec(),
  1304.         ], response$response);
  1305.     }
  1306.     #[ParamConverter("city"converter"city_converter")]
  1307.     public function listByGender(City $citystring $genderDefaultCityProvider $defaultCityProvider): Response
  1308.     {
  1309.         if($city->getId() != $defaultCityProvider->getDefaultCity()->getId()) {
  1310.             throw $this->createNotFoundException();
  1311.         }
  1312.         if(null === Genders::getValueByUriIdentity($gender))
  1313.             throw $this->createNotFoundException();
  1314.         $specs $this->profileListSpecificationService->listByGender($gender);
  1315.         $result $this->listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement($city$specs->spec(), $specs->additionalSpecs(), $specs->genders());
  1316.         return $this->render('ProfileList/list.html.twig', [
  1317.             'profiles' => $result,
  1318.             'recommendationSpec' => $specs->recommendationSpec(),
  1319.         ]);
  1320.     }
  1321.     protected function checkCityAndCountrySource(Page $resultCity $city): Page
  1322.     {
  1323.         if(($result && $result->count() != 0) || false == $this->features->fill_empty_profile_list())
  1324.             return $result;
  1325.         $this->source self::RESULT_SOURCE_CITY;
  1326.         $result $this->listRandomSinglePage($citynullnullnulltruefalse);
  1327.         if($result->count() == 0) {
  1328.             $this->source self::RESULT_SOURCE_COUNTRY;
  1329.             $result $this->listRandomSinglePage($city$city->getCountryCode(), nullnulltruefalse);
  1330.         }
  1331.         return $result;
  1332.     }
  1333.     protected function checkEmptyResultNotMasseur(Page $resultCity $city, ?OrX $alternativeSpecstring $source): Page
  1334.     {
  1335.         if($result->count() != || false == $this->features->fill_empty_profile_list())
  1336.             return $result;
  1337.         if(null != $alternativeSpec) {
  1338.             $this->source $source;
  1339.             $result $this->listRandomSinglePage($citynull$alternativeSpecnulltruefalse);
  1340.         }
  1341.         if($result->count() == 0)
  1342.             $result $this->checkCityAndCountrySource($result$city);
  1343.         return $result;
  1344.     }
  1345.     /**
  1346.      * Сейчас не используется, решили доставать их всех соседних подкатегорий разом.
  1347.      * Пока оставил, вдруг передумают.
  1348.      * @deprecated
  1349.      */
  1350.     public function listByNextSimilarCategories(callable $listMethod$requestCategory, array $similarItems): ORMQueryResult
  1351.     {
  1352.         $similarItems array_filter($similarItems, function($item) use ($requestCategory): bool {
  1353.             return $item != $requestCategory;
  1354.         });
  1355.         //shuffle($similarItems);
  1356.         $item null$result null;
  1357.         do {
  1358.             $item $item == null current($similarItems) : next($similarItems);
  1359.             if(false === $item)
  1360.                 return $result;
  1361.             $result $listMethod($item);
  1362.         } while($result->count() == 0);
  1363.         return $result;
  1364.     }
  1365.     protected function getCurrentPageNumber(): int
  1366.     {
  1367.         $page = (int) $this->requestStack->getCurrentRequest()?->get($this->pageParameter1);
  1368.         if ($page 1) {
  1369.             $page 1;
  1370.         }
  1371.         return $page;
  1372.     }
  1373.     protected function render(string $view, array $parameters = [], Response $response null): Response
  1374.     {
  1375.         $this->listingService->setCurrentListingPage($parameters['profiles']);
  1376.         $requestAttrs $this->requestStack->getCurrentRequest();
  1377.         $listing $requestAttrs->get('_controller');
  1378.         $listing is_array($listing) ? $listing[count($listing) - 1] : $listing;
  1379.         $listing preg_replace('/[^:]+::/'''$listing);
  1380.         $listingParameters $requestAttrs->get('_route_params');
  1381.         $listingParameters is_array($listingParameters) ? $listingParameters : [];
  1382.         $mainRequestHasPageParam = isset(($this->requestStack->getMainRequest()->get('_route_params') ?? [])['page']);
  1383.         if($this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
  1384.             $view = (
  1385.                 str_starts_with($listing'list')
  1386.                 && 'ProfileList/list.html.twig' === $view
  1387.                 && $mainRequestHasPageParam //isset($listingParameters['page'])
  1388.             )
  1389.                 ? 'ProfileList/list.profiles.html.twig'
  1390.                 $view
  1391.             ;
  1392.             return $this->prepareForXhr(parent::render($view$parameters$response));
  1393.             //return $this->getJSONResponse($parameters);
  1394.         } else {
  1395.             $parameters array_merge($parameters, [
  1396.                 'listing' => $listing,
  1397.                 'listing_parameters' => $listingParameters,
  1398.             ]);
  1399.             return parent::render($view$parameters$response);
  1400.         }
  1401.     }
  1402.     private function listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacement(
  1403.         City $city, ?Filter $spec, array $additionalSpecs null, array $genders = [Genders::FEMALE]
  1404.     ): array|Page
  1405.     {
  1406.         return $this->profileList->listActiveWithinCityOrderedByStatusWithSpec($city$spec$additionalSpecs$genders$this->getCurrentPageNumber() < 2);
  1407.     }
  1408.     private function listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited(
  1409.         City $city, ?Filter $spec, array $additionalSpecs null, array $genders = [Genders::FEMALE], int $limit 0,
  1410.     ): array|Page
  1411.     {
  1412.         return $this->profileList->listActiveWithinCityOrderedByStatusWithSpecLimited($city$spec$additionalSpecs$genderstrue$limit);
  1413.     }
  1414.     private function listRandomSinglePage(
  1415.         City $city, ?string $country, ?Filter $spec, ?array $additionalSpecsbool $active, ?bool $masseur false,
  1416.         array $genders = [Genders::FEMALE]
  1417.     ): Page
  1418.     {
  1419.         return $this->profileList->listRandom($city$country$spec$additionalSpecs$active$masseur$genderstrue);
  1420.     }
  1421. //    protected function getJSONResponse(array $parameters)
  1422. //    {
  1423. //        $request = $this->request;
  1424. //        $data = json_decode($request->getContent(), true);
  1425. //
  1426. //        $imageSize = !empty($data['imageSize']) ? $data['imageSize'] : "357x500";
  1427. //
  1428. //        /** @var FakeORMQueryPage $queryPage */
  1429. //        $queryPage = $parameters['profiles'];
  1430. //
  1431. //        $profiles = array_map(function(ProfileListingReadModel $profile) use ($imageSize) {
  1432. //            $profile->stations = array_values($profile->stations);
  1433. //            $profile->avatar['path'] = $this->responsiveAssetsService->getResponsiveImageUrl($profile->avatar['path'], 'profile_media', $imageSize, 'jpg');
  1434. //            $profile->uri = $this->generateUrl('profile_preview.page', ['city' => $profile->city->uriIdentity, 'profile' => $profile->uriIdentity]);
  1435. //            return $profile;
  1436. //        }, $queryPage->getArray());
  1437. //
  1438. //        return new JsonResponse([
  1439. //            'profiles' => $profiles,
  1440. //            'currentPage' => $queryPage->getCurrentPage(),
  1441. //        ], Response::HTTP_OK);
  1442. //    }
  1443. }