src/Controller/ProfileListController.php line 182

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