src/Controller/ProfileListController.php line 152

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