src/Controller/ProfileListController.php line 2821

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\Attribute\SplitFirstPagePagination;
  9. use App\Bridge\Porpaginas\Doctrine\ORM\FakeORMQueryPage;
  10. use App\Dto\ProfileListSpecification;
  11. use App\Entity\Location\City;
  12. use App\Entity\Location\County;
  13. use App\Entity\Location\District;
  14. use App\Entity\Location\Station;
  15. use App\Entity\Profile\Appearances;
  16. use App\Entity\Profile\BodyTypes;
  17. use App\Entity\Profile\BreastTypes;
  18. use App\Entity\Profile\Genders;
  19. use App\Entity\Profile\HairColors;
  20. use App\Entity\Profile\Nationalities;
  21. use App\Entity\Profile\ProfileInventoryItem;
  22. use App\Entity\Profile\ProfileInventoryItemType;
  23. use App\Entity\Profile\PrivateHaircuts;
  24. use App\Entity\Service;
  25. use App\Entity\ServiceGroups;
  26. use App\Entity\TakeOutLocations;
  27. use App\Repository\CityRepository;
  28. use App\Repository\ProfileRepository;
  29. use App\Repository\ServiceRepository;
  30. use App\Repository\StationRepository;
  31. use App\Service\CountryCurrencyResolver;
  32. use App\Service\DefaultCityProvider;
  33. use App\Service\EntitySortService;
  34. use App\Service\Features;
  35. use App\Service\HomepageCityListingsBlockProvider;
  36. use App\Service\ListingRotationApi;
  37. use App\Service\ListingService;
  38. use App\Service\LocationsProfileCounters;
  39. use App\Service\ProfileList;
  40. use App\Service\ProfileListingAppliedFiltersResolver;
  41. use App\Service\ProfileListingRecommendationsFiller;
  42. use App\Service\ProfileListingDataCreator;
  43. use App\Service\ProfileListSpecificationService;
  44. use App\Service\ProfileFilterService;
  45. use App\Service\ProfileNameLandingProvider;
  46. use App\Service\ProfileTopBoard;
  47. use App\Service\Top100ProfilesService;
  48. use App\Specification\ElasticSearch\ISpecification;
  49. use App\Specification\Profile\ProfileHasApartments;
  50. use App\Specification\Profile\ProfileHasComments;
  51. use App\Specification\Profile\ProfileHasVideo;
  52. use App\Specification\Profile\ProfileIdIn;
  53. use App\Specification\Profile\ProfileIdINOrderedByINValues;
  54. use App\Specification\Profile\ProfileIdNotIn;
  55. use App\Specification\Profile\ProfileIsApproved;
  56. use App\Specification\Profile\ProfileIsElite;
  57. use App\Specification\Profile\ProfileIsLocated;
  58. use App\Specification\Profile\ProfileIsProvidingOneOfServices;
  59. use App\Specification\Profile\ProfileIsProvidingTakeOut;
  60. use App\Specification\Profile\ProfilePriceThresholds;
  61. use App\Specification\Profile\ProfileWithAge;
  62. use App\Specification\Profile\ProfileWithAppearance;
  63. use App\Specification\Profile\ProfileWithBodyType;
  64. use App\Specification\Profile\ProfileWithBreastType;
  65. use App\Specification\Profile\ProfileWithHairColor;
  66. use App\Specification\Profile\ProfileWithNationality;
  67. use App\Specification\Profile\ProfileWithPrivateHaircut;
  68. use Flagception\Bundle\FlagceptionBundle\Annotations\Feature;
  69. use Happyr\DoctrineSpecification\Filter\Filter;
  70. use Happyr\DoctrineSpecification\Logic\OrX;
  71. use OpenApi\Attributes as OA;
  72. use Porpaginas\Doctrine\ORM\ORMQueryResult;
  73. use Porpaginas\Page;
  74. use Psr\Cache\CacheItemPoolInterface;
  75. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
  76. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity;
  77. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  78. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  79. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  80. use Symfony\Component\HttpFoundation\JsonResponse;
  81. use Symfony\Component\HttpFoundation\Request;
  82. use Happyr\DoctrineSpecification\Spec;
  83. use Symfony\Component\HttpFoundation\RequestStack;
  84. use Symfony\Component\HttpFoundation\Response;
  85. use Symfony\Component\Intl\Countries;
  86. use Symfony\Component\Routing\Annotation\Route;
  87. use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
  88. /**
  89.  * @see \App\Console\Export\ExportRotationListingsConfigCommand for listing API endpoints
  90.  */
  91. #[Cache(maxage: 60, public: true)]
  92. class ProfileListController extends AbstractController
  93. {
  94.     use ExtendedPaginationTrait;
  95.     use SpecTrait;
  96.     use ProfileMinPriceTrait;
  97.     use ResponseTrait;
  98.     const ENTRIES_ON_PAGE = 36;
  99.     private const NEW_PROFILES_LIMIT = 100;
  100.     const RESULT_SOURCE_COUNTY = 'county';
  101.     const RESULT_SOURCE_DISTRICT = 'district';
  102.     const RESULT_SOURCE_STATION = 'station';
  103.     const RESULT_SOURCE_APPROVED = 'approved';
  104.     const RESULT_SOURCE_WITH_COMMENTS = 'with_comments';
  105.     const RESULT_SOURCE_WITH_VIDEO = 'with_video';
  106.     const RESULT_SOURCE_WITH_SELFIE = 'with_selfie';
  107.     const RESULT_SOURCE_TOP_100 = 'top_100';
  108.     const RESULT_SOURCE_ELITE = 'elite';
  109.     const RESULT_SOURCE_MASSEURS = 'masseurs';
  110.     const RESULT_SOURCE_MASSAGE_SERVICE = 'massage_service';
  111.     const RESULT_SOURCE_BY_PARAMS = 'by_params';
  112.     const RESULT_SOURCE_SERVICE = 'service';
  113.     const RESULT_SOURCE_PROFILE_INVENTORY_ITEM = 'profile_inventory_item';
  114.     const RESULT_SOURCE_CITY = 'city';
  115.     const RESULT_SOURCE_COUNTRY = 'country';
  116.     const RESULT_SOURCE_WITH_WHATSAPP = 'with_whatsapp';
  117.     const RESULT_SOURCE_WITH_TELEGRAM = 'with_telegram';
  118.     const RESULT_SOURCE_WITH_MAX = 'with_max';
  119.     const RESULT_SOURCE_EIGHTEEN_YEARS_OLD = 'eighteen_years_old';
  120.     const RESULT_SOURCE_BIG_ASS = 'big_ass';
  121.     const RESULT_SOURCE_WITH_TATTOO = 'with_tattoo';
  122.     const RESULT_SOURCE_WITH_PIERCING = 'with_piercing';
  123.     const RESULT_SOURCE_ROUND_THE_CLOCK = 'round_the_clock';
  124.     const RESULT_SOURCE_FOR_TWO_HOURS = 'for_two_hours';
  125.     const RESULT_SOURCE_FOR_HOUR = 'for_hour';
  126.     const RESULT_SOURCE_EXPRESS_PROGRAM = 'express_program';
  127.     const RESULT_SOURCE_EROMASSAGE = 'eromassage';
  128.     const RESULT_SOURCE_VERIFIED = 'verified';
  129.     const RESULT_SOURCE_CHEAP = 'cheap';
  130.     const RESULT_SOURCE_MATURE = 'mature';
  131.     const RESULT_SOURCE_UZBEK = 'uzbek';
  132.     const RESULT_SOURCE_INDI_MAIN = 'indi_main';
  133.     private ?string $source = null;
  134.     public function __construct(
  135.         private RequestStack $requestStack,
  136.         private ProfileList                     $profileList,
  137.         private CountryCurrencyResolver         $countryCurrencyResolver,
  138.         private ServiceRepository               $serviceRepository,
  139.         private ListingService                  $listingService,
  140.         private Features                        $features,
  141.         private ProfileFilterService            $profilesFilterService,
  142.         private ProfileListSpecificationService $profileListSpecificationService,
  143.         private ProfileListingDataCreator       $profileListingDataCreator,
  144.         private Top100ProfilesService           $top100ProfilesService,
  145.         private CacheItemPoolInterface          $stationAddedProfilesCache,
  146.         private ParameterBagInterface           $parameterBag,
  147.         private ListingRotationApi              $listingRotationApi,
  148.         private ProfileTopBoard                 $profileTopBoard,
  149.         private HomepageCityListingsBlockProvider $homepageCityListingsBlockProvider,
  150.         private ProfileListingRecommendationsFiller $profileListingRecommendationsFiller,
  151.         private ProfileListingAppliedFiltersResolver $listingAppliedFiltersResolver,
  152.         private NormalizerInterface $normalizer,
  153.         private ProfileNameLandingProvider $profileNameLandingProvider,
  154.     ) {}
  155.     /**
  156.      * @param Page|array $result
  157.      */
  158.     protected function jsonListingResponse($result, City $city, ?ProfileListSpecification $specs = null, array $extra = []): JsonResponse
  159.     {
  160.         $filters = $this->listingAppliedFiltersResolver->resolve($city, $specs);
  161.         if (is_array($result)) {
  162.             return $this->json(array_merge(['filters' => $filters], $result, $extra));
  163.         }
  164.         $normalized = $this->normalizer->normalize($result);
  165.         if (!is_array($normalized)) {
  166.             $normalized = ['data' => $normalized];
  167.         }
  168.         return $this->json(array_merge(['filters' => $filters], $normalized, $extra));
  169.     }
  170.     /**
  171.      * @Feature("has_masseurs")
  172.      */
  173.     #[ParamConverter("city", converter: "city_converter")]
  174.     #[Route("/api/profiles/listing/city/{city}/masseur", name: "api.profile_listing.masseur", methods: "GET", format: "json")]
  175.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  176.     #[OA\Tag(name: "ProfileListing")]
  177.     #[OA\Response(
  178.         response: 200,
  179.         description: '',
  180.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  181.     )]
  182.     public function listForMasseur(Request $request, City $city, ServiceRepository $serviceRepository): Response
  183.     {
  184.         $specs = $this->profileListSpecificationService->listForMasseur($city);
  185.         $response = new Response();
  186.         $massageGroupServices = $serviceRepository->findBy(['group' => ServiceGroups::MASSAGE]);
  187.         $alternativeSpec = $this->getORSpecForItemsArray([$massageGroupServices], function($item): ProfileIsProvidingOneOfServices {
  188.             return new ProfileIsProvidingOneOfServices($item);
  189.         });
  190.         $result = $this->paginatedListing($city, '/city/{city}/masseur', ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_MASSAGE_SERVICE, $response);
  191.         if ($this->isApiRequest($request)) {
  192.             return $this->jsonListingResponse($result, $city, $specs);
  193.         }
  194.         return $this->render('ProfileList/list.html.twig', [
  195.             'profiles' => $result,
  196.             'source' => $this->source,
  197.             'source_default' => self::RESULT_SOURCE_MASSEURS,
  198.             'recommendationSpec' => $specs->recommendationSpec(),
  199.         ], response: $response);
  200.     }
  201.     /**
  202.      * @Feature("extra_category_big_ass")
  203.      */
  204.     #[ParamConverter("city", converter: "city_converter")]
  205.     #[Route("/api/profiles/listing/city/{city}/category/big_ass", name: "api.profile_listing.category.big_ass", methods: "GET", format: "json")]
  206.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  207.     #[OA\Tag(name: "ProfileListing")]
  208.     #[OA\Response(
  209.         response: 200,
  210.         description: '',
  211.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  212.     )]
  213.     public function listBigAss(Request $request, City $city): Response
  214.     {
  215.         $specs = $this->profileListSpecificationService->listBigAss();
  216.         $response = new Response();
  217.         $result = $this->paginatedListing($city, '/city/{city}/category/big_ass', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  218.         if ($this->isApiRequest($request)) {
  219.             return $this->jsonListingResponse($result, $city, $specs);
  220.         }
  221.         return $this->render('ProfileList/list.html.twig', [
  222.             'profiles' => $result,
  223.             'source' => $this->source,
  224.             'source_default' => self::RESULT_SOURCE_BIG_ASS,
  225.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  226.                 'city' => $city->getUriIdentity(),
  227.                 'page' => $this->getCurrentPageNumber(),
  228.             ]),
  229.             'recommendationSpec' => $specs->recommendationSpec(),
  230.         ], response: $response);
  231.     }
  232.     public function listByDefaultCity(ParameterBagInterface $parameterBag, Request $request): Response
  233.     {
  234.         $controller = get_class($this).'::listByCity';
  235.         $path = [
  236.             'city' => $parameterBag->get('default_city'),
  237.             'subRequest' => true,
  238.         ];
  239.         //чтобы в обработчике можно было понять, по какому роуту зашли
  240.         $request->request->set('_route', 'profile_list.list_by_city');
  241.         return $this->forward($controller, $path);
  242.     }
  243.     private function paginatedListing(
  244.         City $city,
  245.         ?string $apiEndpoint,
  246.         array $apiParams,
  247.         ?Filter $listingSpec = null,
  248.         ?OrX $alternativeSpec = null,
  249.         ?string $alternativeSource = null,
  250.         ?Response $response = null,
  251.         bool $fallbackWithinCity = true
  252.     ): Page
  253.     {
  254.         $topPlacement = $this->profileTopBoard->topPlacementSatisfiedBy($city, $listingSpec);
  255.         $topPlacement?->setTopCard(); // mark as top card for UI
  256.         $page = $this->getCurrentPageNumber();
  257.         $apiParams['city'] = $city->getId();
  258.         try {
  259.             if (null === $apiEndpoint) {
  260.                 throw new \RuntimeException('Empty API endpoint to switch to legacy listing query.');
  261.             }
  262.             $result = $this->listingRotationApi->paginate($apiEndpoint, $apiParams, $page, $topPlacement);
  263.             $response?->setMaxAge(10);
  264.         } catch (\Exception) {
  265.             if ($fallbackWithinCity) {
  266.                 $avoidOrTopPlacement = (null !== $topPlacement && $page < 2) ? $topPlacement : null;
  267.                 $result = $this->profileList->list($city, null, $listingSpec, [], true, null, ProfileList::ORDER_BY_STATUS,
  268.                     null, true, null, [Genders::FEMALE], $avoidOrTopPlacement);
  269.             } else {
  270.                 $result = $this->profileList->listActiveOrderedByStatusWithSpec($listingSpec);
  271.             }
  272.         }
  273.         return $result;
  274.     }
  275.     #[SplitFirstPagePagination]
  276.     #[ParamConverter("city", converter: "city_converter")]
  277.     #[Route("/api/profiles/listing/city/{city}", name: "api.profile_listing.by_city", methods: "GET", format: "json")]
  278.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  279.     #[OA\Tag(name: "ProfileListing")]
  280.     #[OA\Response(
  281.         response: 200,
  282.         description: 'Листинг анкет по городу',
  283.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  284.     )]
  285.     public function listByCity(ParameterBagInterface $parameterBag, Request $request, City $city, bool $subRequest = false): Response
  286.     {
  287.         $page = $this->getCurrentPageNumber();
  288.         if (!$this->isApiRequest($request) && $this->features->redirect_default_city_to_homepage() && false === $subRequest && $city->equals($parameterBag->get('default_city')) && $page < 2) {
  289.             return $this->redirectToRoute('homepage', [], 301);
  290.         }
  291.         $specs = $this->profileListSpecificationService->listByCity();
  292.         $response = new Response();
  293.         $result = $this->paginatedListing($city, '/city/{city}', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  294.         if ($this->isApiRequest($request)) {
  295.             return $this->jsonListingResponse($result, $city, $specs);
  296.         }
  297.         $homepageCityListingsBlock = null;
  298.         if ($this->shouldShowHomepageCityListingsBlock($city, $page, $subRequest)) {
  299.             $homepageCityListingsBlock = $this->homepageCityListingsBlockProvider->getForCity($city);
  300.         }
  301.         return $this->render('ProfileList/list.html.twig', [
  302.             'profiles' => $result,
  303.             'homepage_city_listings_block' => $homepageCityListingsBlock,
  304.             'recommendationSpec' => $specs->recommendationSpec(),
  305.         ], response: $response);
  306.     }
  307.     /**
  308.      * @Feature("indi_main")
  309.      */
  310.     #[ParamConverter("city", converter: "city_converter")]
  311.     #[Route("/api/profiles/listing/city/{city}/category/individualki", name: "api.profile_listing.category.individualki", methods: "GET", format: "json")]
  312.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  313.     #[OA\Tag(name: "ProfileListing")]
  314.     #[OA\Response(
  315.         response: 200,
  316.         description: '',
  317.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  318.     )]
  319.     public function listIndiMain(Request $request, City $city): Response
  320.     {
  321.         $specs = $this->profileListSpecificationService->listIndiMain($city);
  322.         $response = new Response();
  323.         $result = $this->paginatedListing($city, '/city/{city}/category/individualki', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  324.         if ($this->isApiRequest($request)) {
  325.             return $this->jsonListingResponse($result, $city, $specs);
  326.         }
  327.         $request->attributes->set('profiles_count', $result->totalCount());
  328.         $request->attributes->set('listingTypeName', self::RESULT_SOURCE_INDI_MAIN);
  329.         $request->attributes->set('city', $city);
  330.         return $this->render('ProfileList/list.html.twig', [
  331.             'profiles' => $result,
  332.             'source' => $this->source,
  333.             'source_default' => self::RESULT_SOURCE_INDI_MAIN,
  334.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  335.                 'city' => $city->getUriIdentity(),
  336.                 'page' => $this->getCurrentPageNumber(),
  337.             ]),
  338.             'recommendationSpec' => $specs->recommendationSpec(),
  339.         ], response: $response);
  340.     }
  341.     /**
  342.      * @Feature("intim_moscow_listing")
  343.      */
  344.     #[Cache(maxage: 3600, public: true)]
  345.     #[Route("/api/profiles/listing/city/{city}/intim", name: "api.profile_listing.intim", methods: "GET", format: "json")]
  346.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  347.     #[OA\Tag(name: "ProfileListing")]
  348.     #[OA\Response(
  349.         response: 200,
  350.         description: '',
  351.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  352.     )]
  353.     public function listIntim(Request $request, DefaultCityProvider $defaultCityProvider): Response
  354.     {
  355.         $city = $defaultCityProvider->getDefaultCity();
  356.         $request->attributes->set('city', $city);
  357.         $specs = $this->profileListSpecificationService->listByCity();
  358.         $response = new Response();
  359.         $result = $this->paginatedListing($city, '/city/{city}/intim', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  360.         $result = $this->shuffleProfilesOnPage($result);
  361.         $response->setMaxAge(3600);
  362.         if ($this->isApiRequest($request)) {
  363.             return $this->jsonListingResponse($result, $city, $specs);
  364.         }
  365.         return $this->render('ProfileList/list.html.twig', [
  366.             'profiles' => $result,
  367.             'city' => $city,
  368.             'recommendationSpec' => $specs->recommendationSpec(),
  369.         ], response: $response);
  370.     }
  371.     #[ParamConverter("city", converter: "city_converter")]
  372.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  373.     #[Route("/api/profiles/listing/city/{city}/county/{county}", name: "api.profile_listing.by_county", methods: "GET", format: "json")]
  374.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  375.     #[OA\Tag(name: "ProfileListing")]
  376.     #[OA\Response(
  377.         response: 200,
  378.         description: '',
  379.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  380.     )]
  381.     public function listByCounty(Request $request, City $city, County $county): Response
  382.     {
  383.         if (!$city->hasCounty($county)) {
  384.             throw $this->createNotFoundException();
  385.         }
  386.         $specs = $this->profileListSpecificationService->listByCounty($county);
  387.         $response = new Response();
  388.         $alternativeSpec = Spec::orX(ProfileIsLocated::withinCounties($city, $city->getCounties()->toArray()));
  389.         $result = $this->paginatedListing($city, '/city/{city}/county/{county}', ['city' => $city->getId(), 'county' => $county->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_COUNTY, $response);
  390.         if ($this->isApiRequest($request)) {
  391.             return $this->jsonListingResponse($result, $city, $specs);
  392.         }
  393.         return $this->render('ProfileList/list.html.twig', [
  394.             'profiles' => $result,
  395.             'source' => $this->source,
  396.             'source_default' => self::RESULT_SOURCE_COUNTY,
  397.             'county' => $county,
  398.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  399.                 'city' => $city->getUriIdentity(),
  400.                 'county' => $county->getUriIdentity(),
  401.                 'page' => $this->getCurrentPageNumber()
  402.             ]),
  403.             'recommendationSpec' => $specs->recommendationSpec(),
  404.         ], response: $response);
  405.     }
  406.     #[ParamConverter("city", converter: "city_converter")]
  407.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  408.     #[Route("/api/profiles/listing/city/{city}/district/{district}", name: "api.profile_listing.by_district", methods: "GET", format: "json")]
  409.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  410.     #[OA\Tag(name: "ProfileListing")]
  411.     #[OA\Response(
  412.         response: 200,
  413.         description: '',
  414.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  415.     )]
  416.     public function listByDistrict(Request $request, City $city, District $district): Response
  417.     {
  418.         if (!$city->hasDistrict($district)) {
  419.             throw $this->createNotFoundException();
  420.         }
  421.         $specs = $this->profileListSpecificationService->listByDistrict($district);
  422.         $response = new Response();
  423.         $alternativeSpec = Spec::orX(ProfileIsLocated::withinDistricts($city, $city->getDistricts()->toArray()));
  424.         $result = $this->paginatedListing($city, '/city/{city}/district/{district}', ['city' => $city->getId(), 'district' => $district->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_DISTRICT, $response);
  425.         if ($this->isApiRequest($request)) {
  426.             return $this->jsonListingResponse($result, $city, $specs);
  427.         }
  428.         return $this->render('ProfileList/list.html.twig', [
  429.             'profiles' => $result,
  430.             'source' => $this->source,
  431.             'source_default' => self::RESULT_SOURCE_DISTRICT,
  432.             'district' => $district,
  433.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  434.                 'city' => $city->getUriIdentity(),
  435.                 'district' => $district->getUriIdentity(),
  436.                 'page' => $this->getCurrentPageNumber()
  437.             ]),
  438.             'recommendationSpec' => $specs->recommendationSpec(),
  439.         ], response: $response);
  440.     }
  441.     /**
  442.      * @Feature("extra_category_without_prepayment")
  443.      */
  444.     #[ParamConverter("city", converter: "city_converter")]
  445.     #[Route("/api/profiles/listing/city/{city}/category/without_prepayment", name: "api.profile_listing.category.without_prepayment", methods: "GET", format: "json")]
  446.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  447.     #[OA\Tag(name: "ProfileListing")]
  448.     #[OA\Response(
  449.         response: 200,
  450.         description: '',
  451.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  452.     )]
  453.     public function listWithoutPrepayment(Request $request, City $city): Response
  454.     {
  455.         $listingData = $this->profileListingDataCreator->getListingDataForFilter('listWithoutPrepayment', [], $city);
  456.         $specs = $listingData['specs'];
  457.         $listingTypeName = $listingData['listingTypeName'];
  458.         $response = new Response();
  459.         $result = $this->paginatedListing($city, '/city/{city}/category/without_prepayment', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  460.         if ($this->isApiRequest($request)) {
  461.             return $this->jsonListingResponse($result, $city, $specs);
  462.         }
  463.         $request->attributes->set('profiles_count', $result->totalCount());
  464.         $request->attributes->set('listingTypeName', $listingTypeName);
  465.         $request->attributes->set('city', $city);
  466.         return $this->render('ProfileList/list.html.twig', [
  467.             'profiles' => $result,
  468.             'source' => $this->source,
  469.             'source_default' => 'without_prepayment',
  470.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  471.                 'city' => $city->getUriIdentity(),
  472.                 'page' => $this->getCurrentPageNumber(),
  473.             ]),
  474.             'recommendationSpec' => $specs->recommendationSpec(),
  475.         ], response: $response);
  476.     }
  477.     #[ParamConverter("city", converter: "city_converter")]
  478.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  479.     #[Route("/api/profiles/listing/city/{city}/station/{station}", name: "api.profile_listing.by_station", methods: "GET", format: "json")]
  480.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  481.     #[OA\Tag(name: "ProfileListing")]
  482.     #[OA\Response(
  483.         response: 200,
  484.         description: '',
  485.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  486.     )]
  487.     public function listByStation(Request $request, City $city, Station $station): Response
  488.     {
  489.         if (!$city->hasStation($station)) {
  490.             throw $this->createNotFoundException();
  491.         }
  492.         $specs = $this->profileListSpecificationService->listByStation($station);
  493.         $response = new Response();
  494.         $result = $this->paginatedListing($city, '/city/{city}/station/{station}', ['city' => $city->getId(), 'station' => $station->getId()], $specs->spec(), null, null, $response);
  495.         if ($this->isApiRequest($request)) {
  496.             return $this->jsonListingResponse($result, $city, $specs);
  497.         }
  498.         return $this->render('ProfileList/list.html.twig', [
  499.             'profiles' => $result,
  500.             'source' => $this->source,
  501.             'source_default' => self::RESULT_SOURCE_STATION,
  502.             'station' => $station,
  503.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  504.                 'city' => $city->getUriIdentity(),
  505.                 'station' => $station->getUriIdentity(),
  506.                 'page' => $this->getCurrentPageNumber()
  507.             ]),
  508.             'recommendationSpec' => $specs->recommendationSpec(),
  509.         ], response: $response);
  510.     }
  511.     #[ParamConverter("city", converter: "city_converter")]
  512.     public function listByStations(City $city, string $stations, StationRepository $stationRepository): Response
  513.     {
  514.         $stationIds = explode(',', $stations);
  515.         $stations = $stationRepository->findBy(['uriIdentity' => $stationIds]);
  516.         $specs = $this->profileListSpecificationService->listByStations($stations);
  517.         $response = new Response();
  518.         $result = $this->paginatedListing($city, null, [], $specs->spec(), null, null, $response);
  519.         return $this->render('ProfileList/list.html.twig', [
  520.             'profiles' => $result,
  521.             'recommendationSpec' => $specs->recommendationSpec(),
  522.         ]);
  523.     }
  524.     #[ParamConverter("city", converter: "city_converter")]
  525.     #[Route("/api/profiles/listing/city/{city}/approved", name: "api.profile_listing.approved", methods: "GET", format: "json")]
  526.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  527.     #[OA\Tag(name: "ProfileListing")]
  528.     #[OA\Response(
  529.         response: 200,
  530.         description: '',
  531.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  532.     )]
  533.     public function listApproved(Request $request, City $city): Response
  534.     {
  535.         $specs = $this->profileListSpecificationService->listApproved();
  536.         $response = new Response();
  537.         $result = $this->paginatedListing($city, '/city/{city}/approved', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  538.         if ($this->isApiRequest($request)) {
  539.             return $this->jsonListingResponse($result, $city, $specs);
  540.         }
  541.         return $this->render('ProfileList/list.html.twig', [
  542.             'profiles' => $result,
  543.             'source' => $this->source,
  544.             'source_default' => self::RESULT_SOURCE_APPROVED,
  545.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  546.                 'city' => $city->getUriIdentity(),
  547.                 'page' => $this->getCurrentPageNumber()
  548.             ]),
  549.             'recommendationSpec' => $specs->recommendationSpec(),
  550.         ], response: $response);
  551.     }
  552.     /**
  553.      * @Feature("extra_category_with_whatsapp")
  554.      */
  555.     #[ParamConverter("city", converter: "city_converter")]
  556.     #[Route("/api/profiles/listing/city/{city}/category/whatsapp", name: "api.profile_listing.category.whatsapp", methods: "GET", format: "json")]
  557.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  558.     #[OA\Tag(name: "ProfileListing")]
  559.     #[OA\Response(
  560.         response: 200,
  561.         description: '',
  562.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  563.     )]
  564.     public function listWithWhatsapp(Request $request, City $city): Response
  565.     {
  566.         $specs = $this->profileListSpecificationService->listWithWhatsapp();
  567.         $response = new Response();
  568.         $result = $this->paginatedListing($city, '/city/{city}/category/whatsapp', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  569.         if ($this->isApiRequest($request)) {
  570.             return $this->jsonListingResponse($result, $city, $specs);
  571.         }
  572.         return $this->render('ProfileList/list.html.twig', [
  573.             'profiles' => $result,
  574.             'source' => $this->source,
  575.             'source_default' => self::RESULT_SOURCE_WITH_WHATSAPP,
  576.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  577.                 'city' => $city->getUriIdentity(),
  578.                 'page' => $this->getCurrentPageNumber(),
  579.             ]),
  580.             'recommendationSpec' => $specs->recommendationSpec(),
  581.         ], response: $response);
  582.     }
  583.     /**
  584.      * @Feature("extra_category_with_telegram")
  585.      */
  586.     #[ParamConverter("city", converter: "city_converter")]
  587.     #[Route("/api/profiles/listing/city/{city}/category/telegram", name: "api.profile_listing.category.telegram", methods: "GET", format: "json")]
  588.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  589.     #[OA\Tag(name: "ProfileListing")]
  590.     #[OA\Response(
  591.         response: 200,
  592.         description: '',
  593.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  594.     )]
  595.     public function listWithTelegram(Request $request, City $city): Response
  596.     {
  597.         $specs = $this->profileListSpecificationService->listWithTelegram();
  598.         $response = new Response();
  599.         $result = $this->paginatedListing($city, '/city/{city}/category/telegram', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  600.         if ($this->isApiRequest($request)) {
  601.             return $this->jsonListingResponse($result, $city, $specs);
  602.         }
  603.         return $this->render('ProfileList/list.html.twig', [
  604.             'profiles' => $result,
  605.             'source' => $this->source,
  606.             'source_default' => self::RESULT_SOURCE_WITH_TELEGRAM,
  607.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  608.                 'city' => $city->getUriIdentity(),
  609.                 'page' => $this->getCurrentPageNumber(),
  610.             ]),
  611.             'recommendationSpec' => $specs->recommendationSpec(),
  612.         ], response: $response);
  613.     }
  614.     /**
  615.      * @Feature("extra_category_with_max")
  616.      */
  617.     #[ParamConverter("city", converter: "city_converter")]
  618.     #[Route("/api/profiles/listing/city/{city}/category/max", name: "api.profile_listing.category.max", methods: "GET", format: "json")]
  619.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  620.     #[OA\Tag(name: "ProfileListing")]
  621.     #[OA\Response(
  622.         response: 200,
  623.         description: '',
  624.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  625.     )]
  626.     public function listWithMax(Request $request, City $city): Response
  627.     {
  628.         $specs = $this->profileListSpecificationService->listWithMax();
  629.         $response = new Response();
  630.         $result = $this->paginatedListing($city, '/city/{city}/category/max', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  631.         if ($this->isApiRequest($request)) {
  632.             return $this->jsonListingResponse($result, $city, $specs);
  633.         }
  634.         return $this->render('ProfileList/list.html.twig', [
  635.             'profiles' => $result,
  636.             'source' => $this->source,
  637.             'source_default' => self::RESULT_SOURCE_WITH_MAX,
  638.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  639.                 'city' => $city->getUriIdentity(),
  640.                 'page' => $this->getCurrentPageNumber(),
  641.             ]),
  642.             'recommendationSpec' => $specs->recommendationSpec(),
  643.         ], response: $response);
  644.     }
  645.     /**
  646.      * @Feature("extra_category_eighteen_years_old")
  647.      */
  648.     #[ParamConverter("city", converter: "city_converter")]
  649.     #[Route("/api/profiles/listing/city/{city}/category/eighteen_years_old", name: "api.profile_listing.category.eighteen_years_old", methods: "GET", format: "json")]
  650.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  651.     #[OA\Tag(name: "ProfileListing")]
  652.     #[OA\Response(
  653.         response: 200,
  654.         description: '',
  655.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  656.     )]
  657.     public function listEighteenYearsOld(Request $request, City $city): Response
  658.     {
  659.         $specs = $this->profileListSpecificationService->listEighteenYearsOld();
  660.         $response = new Response();
  661.         $result = $this->paginatedListing($city, '/city/{city}/category/eighteen_years_old', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  662.         if ($this->isApiRequest($request)) {
  663.             return $this->jsonListingResponse($result, $city, $specs);
  664.         }
  665.         return $this->render('ProfileList/list.html.twig', [
  666.             'profiles' => $result,
  667.             'source' => $this->source,
  668.             'source_default' => self::RESULT_SOURCE_EIGHTEEN_YEARS_OLD,
  669.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  670.                 'city' => $city->getUriIdentity(),
  671.                 'page' => $this->getCurrentPageNumber(),
  672.             ]),
  673.             'recommendationSpec' => $specs->recommendationSpec(),
  674.         ], response: $response);
  675.     }
  676.     /**
  677.      * @Feature("extra_category_rublevskie")
  678.      */
  679.     #[ParamConverter("city", converter: "city_converter")]
  680.     #[Route("/api/profiles/listing/city/{city}/category/rublevskie", name: "api.profile_listing.category.rublevskie", methods: "GET", format: "json")]
  681.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  682.     #[OA\Tag(name: "ProfileListing")]
  683.     #[OA\Response(
  684.         response: 200,
  685.         description: '',
  686.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  687.     )]
  688.     public function listRublevskie(Request $request, City $city): Response
  689.     {
  690.         if ($city->getUriIdentity() !== 'moscow') {
  691.             throw $this->createNotFoundException();
  692.         }
  693.         $specs = $this->profileListSpecificationService->listRublevskie($city);
  694.         $response = new Response();
  695.         $result = $this->paginatedListing($city, '/city/{city}/category/rublevskie', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  696.         if ($this->isApiRequest($request)) {
  697.             return $this->jsonListingResponse($result, $city, $specs);
  698.         }
  699.         return $this->render('ProfileList/list.html.twig', [
  700.             'profiles' => $result,
  701.             'source' => $this->source,
  702.             'source_default' => 'rublevskie',
  703.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  704.                 'city' => $city->getUriIdentity(),
  705.                 'page' => $this->getCurrentPageNumber(),
  706.             ]),
  707.             'recommendationSpec' => $specs->recommendationSpec(),
  708.         ], response: $response);
  709.     }
  710.     /**
  711.      * @Feature("extra_category_with_tattoo")
  712.      */
  713.     #[ParamConverter("city", converter: "city_converter")]
  714.     #[Route("/api/profiles/listing/city/{city}/category/with_tattoo", name: "api.profile_listing.category.with_tattoo", methods: "GET", format: "json")]
  715.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  716.     #[OA\Tag(name: "ProfileListing")]
  717.     #[OA\Response(
  718.         response: 200,
  719.         description: '',
  720.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  721.     )]
  722.     public function listWithTattoo(Request $request, City $city): Response
  723.     {
  724.         $specs = $this->profileListSpecificationService->listWithTattoo();
  725.         $response = new Response();
  726.         $result = $this->paginatedListing($city, '/city/{city}/category/with_tattoo', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  727.         if ($this->isApiRequest($request)) {
  728.             return $this->jsonListingResponse($result, $city, $specs);
  729.         }
  730.         return $this->render('ProfileList/list.html.twig', [
  731.             'profiles' => $result,
  732.             'source' => $this->source,
  733.             'source_default' => self::RESULT_SOURCE_WITH_TATTOO,
  734.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  735.                 'city' => $city->getUriIdentity(),
  736.                 'page' => $this->getCurrentPageNumber(),
  737.             ]),
  738.             'recommendationSpec' => $specs->recommendationSpec(),
  739.         ], response: $response);
  740.     }
  741.     #[ParamConverter("city", converter: "city_converter")]
  742.     #[Route("/api/profiles/listing/city/{city}/with_comments", name: "api.profile_listing.with_comments", methods: "GET", format: "json")]
  743.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  744.     #[OA\Tag(name: "ProfileListing")]
  745.     #[OA\Response(
  746.         response: 200,
  747.         description: '',
  748.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  749.     )]
  750.     public function listWithComments(Request $request, City $city): Response
  751.     {
  752.         $specs = $this->profileListSpecificationService->listWithComments();
  753.         $response = new Response();
  754.         $result = $this->paginatedListing($city, '/city/{city}/with_comments', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  755.         if ($this->isApiRequest($request)) {
  756.             return $this->jsonListingResponse($result, $city, $specs);
  757.         }
  758.         return $this->render('ProfileList/list.html.twig', [
  759.             'profiles' => $result,
  760.             'source' => $this->source,
  761.             'source_default' => self::RESULT_SOURCE_WITH_COMMENTS,
  762.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  763.                 'city' => $city->getUriIdentity(),
  764.                 'page' => $this->getCurrentPageNumber()
  765.             ]),
  766.             'recommendationSpec' => $specs->recommendationSpec(),
  767.         ], response: $response);
  768.     }
  769.     /**
  770.      * @Feature("extra_category_with_piercing")
  771.      */
  772.     #[ParamConverter("city", converter: "city_converter")]
  773.     #[Route("/api/profiles/listing/city/{city}/category/with_piercing", name: "api.profile_listing.category.with_piercing", methods: "GET", format: "json")]
  774.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  775.     #[OA\Tag(name: "ProfileListing")]
  776.     #[OA\Response(
  777.         response: 200,
  778.         description: '',
  779.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  780.     )]
  781.     public function listWithPiercing(Request $request, City $city): Response
  782.     {
  783.         $specs = $this->profileListSpecificationService->listWithPiercing();
  784.         $response = new Response();
  785.         $result = $this->paginatedListing($city, '/city/{city}/category/with_piercing', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  786.         if ($this->isApiRequest($request)) {
  787.             return $this->jsonListingResponse($result, $city, $specs);
  788.         }
  789.         return $this->render('ProfileList/list.html.twig', [
  790.             'profiles' => $result,
  791.             'source' => $this->source,
  792.             'source_default' => self::RESULT_SOURCE_WITH_PIERCING,
  793.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  794.                 'city' => $city->getUriIdentity(),
  795.                 'page' => $this->getCurrentPageNumber(),
  796.             ]),
  797.             'recommendationSpec' => $specs->recommendationSpec(),
  798.         ], response: $response);
  799.     }
  800.     #[ParamConverter("city", converter: "city_converter")]
  801.     #[Route("/api/profiles/listing/city/{city}/with_video", name: "api.profile_listing.with_video", methods: "GET", format: "json")]
  802.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  803.     #[OA\Tag(name: "ProfileListing")]
  804.     #[OA\Response(
  805.         response: 200,
  806.         description: '',
  807.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  808.     )]
  809.     public function listWithVideo(Request $request, City $city): Response
  810.     {
  811.         $specs = $this->profileListSpecificationService->listWithVideo();
  812.         $response = new Response();
  813.         $result = $this->paginatedListing($city, '/city/{city}/with_video', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  814.         if ($this->isApiRequest($request)) {
  815.             return $this->jsonListingResponse($result, $city, $specs);
  816.         }
  817.         return $this->render('ProfileList/list.html.twig', [
  818.             'profiles' => $result,
  819.             'source' => $this->source,
  820.             'source_default' => self::RESULT_SOURCE_WITH_VIDEO,
  821.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  822.                 'city' => $city->getUriIdentity(),
  823.                 'page' => $this->getCurrentPageNumber()
  824.             ]),
  825.             'recommendationSpec' => $specs->recommendationSpec(),
  826.         ], response: $response);
  827.     }
  828.      /**
  829.      * @Feature("extra_category_round_the_clock")
  830.      */
  831.     #[ParamConverter("city", converter: "city_converter")]
  832.     #[Route("/api/profiles/listing/city/{city}/category/round_the_clock", name: "api.profile_listing.category.round_the_clock", methods: "GET", format: "json")]
  833.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  834.     #[OA\Tag(name: "ProfileListing")]
  835.     #[OA\Response(
  836.         response: 200,
  837.         description: '',
  838.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  839.     )]
  840.     public function listRoundTheClock(Request $request, City $city): Response
  841.     {
  842.         $specs = $this->profileListSpecificationService->listRoundTheClock();
  843.         $response = new Response();
  844.         $result = $this->paginatedListing($city, '/city/{city}/category/round_the_clock', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  845.         if ($this->isApiRequest($request)) {
  846.             return $this->jsonListingResponse($result, $city, $specs);
  847.         }
  848.         return $this->render('ProfileList/list.html.twig', [
  849.             'profiles' => $result,
  850.             'source' => $this->source,
  851.             'source_default' => self::RESULT_SOURCE_ROUND_THE_CLOCK,
  852.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  853.                 'city' => $city->getUriIdentity(),
  854.                 'page' => $this->getCurrentPageNumber(),
  855.             ]),
  856.             'recommendationSpec' => $specs->recommendationSpec(),
  857.         ], response: $response);
  858.     }
  859.     /**
  860.      * @Feature("extra_category_for_two_hours")
  861.      */
  862.     #[ParamConverter("city", converter: "city_converter")]
  863.     #[Route("/api/profiles/listing/city/{city}/category/for_two_hours", name: "api.profile_listing.category.for_two_hours", methods: "GET", format: "json")]
  864.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  865.     #[OA\Tag(name: "ProfileListing")]
  866.     #[OA\Response(
  867.         response: 200,
  868.         description: '',
  869.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  870.     )]
  871.     public function listForTwoHours(Request $request, City $city): Response
  872.     {
  873.         $specs = $this->profileListSpecificationService->listForTwoHours();
  874.         $response = new Response();
  875.         $result = $this->paginatedListing($city, '/city/{city}/category/for_two_hours', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  876.         if ($this->isApiRequest($request)) {
  877.             return $this->jsonListingResponse($result, $city, $specs);
  878.         }
  879.         return $this->render('ProfileList/list.html.twig', [
  880.             'profiles' => $result,
  881.             'source' => $this->source,
  882.             'source_default' => self::RESULT_SOURCE_FOR_TWO_HOURS,
  883.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  884.                 'city' => $city->getUriIdentity(),
  885.                 'page' => $this->getCurrentPageNumber(),
  886.             ]),
  887.             'recommendationSpec' => $specs->recommendationSpec(),
  888.         ], response: $response);
  889.     }
  890.     /**
  891.      * @Feature("extra_category_for_hour")
  892.      */
  893.     #[ParamConverter("city", converter: "city_converter")]
  894.     #[Route("/api/profiles/listing/city/{city}/category/for_hour", name: "api.profile_listing.category.for_hour", methods: "GET", format: "json")]
  895.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  896.     #[OA\Tag(name: "ProfileListing")]
  897.     #[OA\Response(
  898.         response: 200,
  899.         description: '',
  900.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  901.     )]
  902.     public function listForHour(Request $request, City $city): Response
  903.     {
  904.         $specs = $this->profileListSpecificationService->listForHour();
  905.         $response = new Response();
  906.         $result = $this->paginatedListing($city, '/city/{city}/category/for_hour', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  907.         if ($this->isApiRequest($request)) {
  908.             return $this->jsonListingResponse($result, $city, $specs);
  909.         }
  910.         return $this->render('ProfileList/list.html.twig', [
  911.             'profiles' => $result,
  912.             'source' => $this->source,
  913.             'source_default' => self::RESULT_SOURCE_FOR_HOUR,
  914.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  915.                 'city' => $city->getUriIdentity(),
  916.                 'page' => $this->getCurrentPageNumber(),
  917.             ]),
  918.             'recommendationSpec' => $specs->recommendationSpec(),
  919.         ], response: $response);
  920.     }
  921.     /**
  922.      * @Feature("extra_category_express_program")
  923.      */
  924.     #[ParamConverter("city", converter: "city_converter")]
  925.     #[Route("/api/profiles/listing/city/{city}/category/express", name: "api.profile_listing.category.express", methods: "GET", format: "json")]
  926.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  927.     #[OA\Tag(name: "ProfileListing")]
  928.     #[OA\Response(
  929.         response: 200,
  930.         description: '',
  931.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  932.     )]
  933.     public function listExpress(Request $request, City $city): Response
  934.     {
  935.         $specs = $this->profileListSpecificationService->listExpress();
  936.         $response = new Response();
  937.         $result = $this->paginatedListing($city, '/city/{city}/category/express', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  938.         if ($this->isApiRequest($request)) {
  939.             return $this->jsonListingResponse($result, $city, $specs);
  940.         }
  941.         return $this->render('ProfileList/list.html.twig', [
  942.             'profiles' => $result,
  943.             'source' => $this->source,
  944.             'source_default' => self::RESULT_SOURCE_EXPRESS_PROGRAM,
  945.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  946.                 'city' => $city->getUriIdentity(),
  947.                 'page' => $this->getCurrentPageNumber(),
  948.             ]),
  949.             'recommendationSpec' => $specs->recommendationSpec(),
  950.         ], response: $response);
  951.     }
  952.     /**
  953.      * @Feature("extra_category_grandmothers")
  954.      */
  955.     #[ParamConverter("city", converter: "city_converter")]
  956.     #[Route("/api/profiles/listing/city/{city}/category/grandmothers", name: "api.profile_listing.category.grandmothers", methods: "GET", format: "json")]
  957.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  958.     #[OA\Tag(name: "ProfileListing")]
  959.     #[OA\Response(
  960.         response: 200,
  961.         description: '',
  962.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  963.     )]
  964.     public function listGrandmothers(Request $request, City $city): Response
  965.     {
  966.         $specs = $this->profileListSpecificationService->listGrandmothers();
  967.         $response = new Response();
  968.         $result = $this->paginatedListing($city, '/city/{city}/category/grandmothers', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  969.         if ($this->isApiRequest($request)) {
  970.             return $this->jsonListingResponse($result, $city, $specs);
  971.         }
  972.         return $this->render('ProfileList/list.html.twig', [
  973.             'profiles' => $result,
  974.             'source' => $this->source,
  975.             'source_default' => 'grandmothers',
  976.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  977.                 'city' => $city->getUriIdentity(),
  978.                 'page' => $this->getCurrentPageNumber(),
  979.             ]),
  980.             'recommendationSpec' => $specs->recommendationSpec(),
  981.         ], response: $response);
  982.     }
  983.         /**
  984.      * @Feature("extra_category_big_breast")
  985.      */
  986.     #[ParamConverter("city", converter: "city_converter")]
  987.     #[Route("/api/profiles/listing/city/{city}/category/big_breast", name: "api.profile_listing.category.big_breast", methods: "GET", format: "json")]
  988.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  989.     #[OA\Tag(name: "ProfileListing")]
  990.     #[OA\Response(
  991.         response: 200,
  992.         description: '',
  993.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  994.     )]
  995.     public function listBigBreast(Request $request, City $city): Response
  996.     {
  997.         $specs = $this->profileListSpecificationService->listBigBreast();
  998.         $response = new Response();
  999.         $result = $this->paginatedListing($city, '/city/{city}/category/big_breast', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1000.         if ($this->isApiRequest($request)) {
  1001.             return $this->jsonListingResponse($result, $city, $specs);
  1002.         }
  1003.         return $this->render('ProfileList/list.html.twig', [
  1004.             'profiles' => $result,
  1005.             'source' => $this->source,
  1006.             'source_default' => 'big_breast',
  1007.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1008.                 'city' => $city->getUriIdentity(),
  1009.                 'page' => $this->getCurrentPageNumber(),
  1010.             ]),
  1011.             'recommendationSpec' => $specs->recommendationSpec(),
  1012.         ], response: $response);
  1013.     }
  1014.     /**
  1015.      * @Feature("extra_category_very_skinny")
  1016.      */
  1017.     #[ParamConverter("city", converter: "city_converter")]
  1018.     #[Route("/api/profiles/listing/city/{city}/category/very_skinny", name: "api.profile_listing.category.very_skinny", methods: "GET", format: "json")]
  1019.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1020.     #[OA\Tag(name: "ProfileListing")]
  1021.     #[OA\Response(
  1022.         response: 200,
  1023.         description: '',
  1024.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1025.     )]
  1026.     public function listVerySkinny(Request $request, City $city): Response
  1027.     {
  1028.         $specs = $this->profileListSpecificationService->listVerySkinny();
  1029.         $response = new Response();
  1030.         $result = $this->paginatedListing($city, '/city/{city}/category/very_skinny', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1031.         if ($this->isApiRequest($request)) {
  1032.             return $this->jsonListingResponse($result, $city, $specs);
  1033.         }
  1034.         return $this->render('ProfileList/list.html.twig', [
  1035.             'profiles' => $result,
  1036.             'source' => $this->source,
  1037.             'source_default' => 'very_skinny',
  1038.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1039.                 'city' => $city->getUriIdentity(),
  1040.                 'page' => $this->getCurrentPageNumber(),
  1041.             ]),
  1042.             'recommendationSpec' => $specs->recommendationSpec(),
  1043.         ], response: $response);
  1044.     }
  1045.     /**
  1046.      * @Feature("extra_category_small_ass")
  1047.      */
  1048.     #[ParamConverter("city", converter: "city_converter")]
  1049.     #[Route("/api/profiles/listing/city/{city}/category/small_ass", name: "api.profile_listing.category.small_ass", methods: "GET", format: "json")]
  1050.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1051.     #[OA\Tag(name: "ProfileListing")]
  1052.     #[OA\Response(
  1053.         response: 200,
  1054.         description: '',
  1055.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1056.     )]
  1057.     public function listSmallAss(Request $request, City $city): Response
  1058.     {
  1059.         $specs = $this->profileListSpecificationService->listSmallAss();
  1060.         $response = new Response();
  1061.         $result = $this->paginatedListing($city, '/city/{city}/category/small_ass', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1062.         if ($this->isApiRequest($request)) {
  1063.             return $this->jsonListingResponse($result, $city, $specs);
  1064.         }
  1065.         return $this->render('ProfileList/list.html.twig', [
  1066.             'profiles' => $result,
  1067.             'source' => $this->source,
  1068.             'source_default' => 'small_ass',
  1069.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1070.                 'city' => $city->getUriIdentity(),
  1071.                 'page' => $this->getCurrentPageNumber(),
  1072.             ]),
  1073.             'recommendationSpec' => $specs->recommendationSpec(),
  1074.         ], response: $response);
  1075.     }
  1076.     /**
  1077.      * @Feature("extra_category_beautiful_prostitutes")
  1078.      */
  1079.     #[ParamConverter("city", converter: "city_converter")]
  1080.     #[Route("/api/profiles/listing/city/{city}/category/beautiful_prostitutes", name: "api.profile_listing.category.beautiful_prostitutes", methods: "GET", format: "json")]
  1081.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1082.     #[OA\Tag(name: "ProfileListing")]
  1083.     #[OA\Response(
  1084.         response: 200,
  1085.         description: '',
  1086.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1087.     )]
  1088.     public function listBeautifulProstitutes(Request $request, City $city): Response
  1089.     {
  1090.         $specs = $this->profileListSpecificationService->listBeautifulProstitutes();
  1091.         $response = new Response();
  1092.         $result = $this->paginatedListing($city, '/city/{city}/category/beautiful_prostitutes', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1093.         if ($this->isApiRequest($request)) {
  1094.             return $this->jsonListingResponse($result, $city, $specs);
  1095.         }
  1096.         return $this->render('ProfileList/list.html.twig', [
  1097.             'profiles' => $result,
  1098.             'source' => $this->source,
  1099.             'source_default' => 'beautiful_prostitutes',
  1100.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1101.                 'city' => $city->getUriIdentity(),
  1102.                 'page' => $this->getCurrentPageNumber(),
  1103.             ]),
  1104.             'recommendationSpec' => $specs->recommendationSpec(),
  1105.         ], response: $response);
  1106.     }
  1107.     /**
  1108.      * @Feature("extra_category_without_intermediaries")
  1109.      */
  1110.     #[ParamConverter("city", converter: "city_converter")]
  1111.     #[Route("/api/profiles/listing/city/{city}/category/without_intermediaries", name: "api.profile_listing.category.without_intermediaries", methods: "GET", format: "json")]
  1112.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1113.     #[OA\Tag(name: "ProfileListing")]
  1114.     #[OA\Response(
  1115.         response: 200,
  1116.         description: '',
  1117.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1118.     )]
  1119.     public function listWithoutIntermediaries(Request $request, City $city): Response
  1120.     {
  1121.         $specs = $this->profileListSpecificationService->listWithoutIntermediaries();
  1122.         $response = new Response();
  1123.         $result = $this->paginatedListing($city, '/city/{city}/category/without_intermediaries', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1124.         if ($this->isApiRequest($request)) {
  1125.             return $this->jsonListingResponse($result, $city, $specs);
  1126.         }
  1127.         return $this->render('ProfileList/list.html.twig', [
  1128.             'profiles' => $result,
  1129.             'source' => $this->source,
  1130.             'source_default' => 'without_intermediaries',
  1131.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1132.                 'city' => $city->getUriIdentity(),
  1133.                 'page' => $this->getCurrentPageNumber(),
  1134.             ]),
  1135.             'recommendationSpec' => $specs->recommendationSpec(),
  1136.         ], response: $response);
  1137.     }
  1138.     #[ParamConverter("city", converter: "city_converter")]
  1139.     #[Route("/api/profiles/listing/city/{city}/list-pages/suburbs", name: "api.profile_listing.list_pages.suburbs", methods: "GET", format: "json")]
  1140.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1141.     #[OA\Tag(name: "ProfileListing")]
  1142.     #[OA\Response(
  1143.         response: 200,
  1144.         description: 'Листинг анкет для страницы списка городов',
  1145.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1146.     )]
  1147.     public function listPageSuburbs(
  1148.         Request $request,
  1149.         City $city,
  1150.         CityRepository $cityRepository,
  1151.         ProfileRepository $profileRepository
  1152.     ): Response
  1153.     {
  1154.         $isApiRequest = $this->isApiRequest($request);
  1155.         $locale = $request->getLocale() ?? 'ru';
  1156.         $countries = Countries::getNames($locale);
  1157.         $countByCity = $profileRepository->countByCity();
  1158.         $profilesOnListPages = null;
  1159.         $specs = null;
  1160.         $response = new Response();
  1161.         $cities = [];
  1162.         foreach ($cityRepository->findAll() as $listCity) {
  1163.             $countryCode = $listCity->getCountryCode();
  1164.             $country = $countries[$countryCode];
  1165.             if (!isset($cities[$country])) {
  1166.                 $cities[$country] = [];
  1167.             }
  1168.             $route = $listCity->equals($city) ? 'homepage' : 'profile_list.list_by_city';
  1169.             $cities[$country][$listCity->getId()] = [
  1170.                 'city' => $listCity,
  1171.                 'count' => $countByCity[$listCity->getId()] ?? 0,
  1172.                 'uri' => $this->generateUrl($route, ['city' => $listCity->getUriIdentity()]),
  1173.             ];
  1174.         }
  1175.         if ($this->features->profiles_on_list_pages()) {
  1176.             $specs = $this->profileListSpecificationService->listPagesWithSuburbs($city);
  1177.             $profilesOnListPages = $this->paginatedListing(
  1178.                 $city,
  1179.                 '/city/{city}/list-pages/suburbs',
  1180.                 ['city' => $city->getId()],
  1181.                 $specs->spec(),
  1182.                 response: $response,
  1183.                 fallbackWithinCity: false
  1184.             );
  1185.         } elseif ($isApiRequest) {
  1186.             throw $this->createNotFoundException();
  1187.         }
  1188.         if ($isApiRequest) {
  1189.             return $this->jsonListingResponse($profilesOnListPages, $city, $specs);
  1190.         }
  1191.         return $this->render('CityList/list_by_country.html.twig', [
  1192.             'cities' => $cities,
  1193.             'profiles' => $profilesOnListPages,
  1194.             'profiles_on_list_pages' => $profilesOnListPages,
  1195.         ], response: $response);
  1196.     }
  1197.     #[ParamConverter("city", converter: "city_converter")]
  1198.     #[Route("/api/profiles/listing/city/{city}/list-pages/stations", name: "api.profile_listing.list_pages.stations", methods: "GET", format: "json")]
  1199.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1200.     #[OA\Tag(name: "ProfileListing")]
  1201.     #[OA\Response(
  1202.         response: 200,
  1203.         description: 'Листинг анкет для страницы списка метро',
  1204.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1205.     )]
  1206.     public function listPageStations(
  1207.         Request $request,
  1208.         City $city,
  1209.         EntitySortService $sortService,
  1210.         LocationsProfileCounters $locationsProfileCounters
  1211.     ): Response
  1212.     {
  1213.         $isApiRequest = $this->isApiRequest($request);
  1214.         $countByStations = $locationsProfileCounters->getCountByStations();
  1215.         $profilesOnListPages = null;
  1216.         $specs = null;
  1217.         $response = new Response();
  1218.         if ($this->features->profiles_on_list_pages()) {
  1219.             $specs = $this->profileListSpecificationService->listPagesWithStations();
  1220.             $profilesOnListPages = $this->paginatedListing(
  1221.                 $city,
  1222.                 '/city/{city}/list-pages/stations',
  1223.                 ['city' => $city->getId()],
  1224.                 $specs->spec(),
  1225.                 response: $response,
  1226.                 fallbackWithinCity: false
  1227.             );
  1228.         } elseif ($isApiRequest) {
  1229.             throw $this->createNotFoundException();
  1230.         }
  1231.         if ($isApiRequest) {
  1232.             return $this->jsonListingResponse($profilesOnListPages, $city, $specs);
  1233.         }
  1234.         return $this->render('StationList/list.html.twig', [
  1235.             'city' => $city,
  1236.             'stations' => $sortService->sortEntityArrayByTranslatableFieldName($city->getStations()->toArray(), 'name'),
  1237.             'count_by_stations' => $countByStations,
  1238.             'profiles' => $profilesOnListPages,
  1239.             'profiles_on_list_pages' => $profilesOnListPages,
  1240.         ], response: $response);
  1241.     }
  1242.     #[ParamConverter("city", converter: "city_converter")]
  1243.     #[Route("/api/profiles/listing/city/{city}/list-pages/districts", name: "api.profile_listing.list_pages.districts", methods: "GET", format: "json")]
  1244.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1245.     #[OA\Tag(name: "ProfileListing")]
  1246.     #[OA\Response(
  1247.         response: 200,
  1248.         description: 'Листинг анкет для страницы списка районов',
  1249.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1250.     )]
  1251.     public function listPageDistricts(
  1252.         Request $request,
  1253.         City $city,
  1254.         LocationsProfileCounters $locationsProfileCounters
  1255.     ): Response
  1256.     {
  1257.         $isApiRequest = $this->isApiRequest($request);
  1258.         if (count($city->getDistricts()) < 1) {
  1259.             if ($isApiRequest) {
  1260.                 throw $this->createNotFoundException();
  1261.             }
  1262.             return $this->redirectToRoute('profile_list.list_by_city', ['city' => $city->getUriIdentity()]);
  1263.         }
  1264.         $countByDistricts = $locationsProfileCounters->getCountByDistricts();
  1265.         $countByCounties = $locationsProfileCounters->getCountByCounties();
  1266.         $profilesOnListPages = null;
  1267.         $specs = null;
  1268.         $response = new Response();
  1269.         if ($this->features->profiles_on_list_pages()) {
  1270.             $specs = $this->profileListSpecificationService->listPagesWithDistricts();
  1271.             $profilesOnListPages = $this->paginatedListing(
  1272.                 $city,
  1273.                 '/city/{city}/list-pages/districts',
  1274.                 ['city' => $city->getId()],
  1275.                 $specs->spec(),
  1276.                 response: $response,
  1277.                 fallbackWithinCity: false
  1278.             );
  1279.         } elseif ($isApiRequest) {
  1280.             throw $this->createNotFoundException();
  1281.         }
  1282.         if ($isApiRequest) {
  1283.             return $this->jsonListingResponse($profilesOnListPages, $city, $specs);
  1284.         }
  1285.         return $this->render('DistrictList/list.html.twig', [
  1286.             'city' => $city,
  1287.             'count_by_districts' => $countByDistricts,
  1288.             'count_by_counties' => $countByCounties,
  1289.             'profiles' => $profilesOnListPages,
  1290.             'profiles_on_list_pages' => $profilesOnListPages,
  1291.         ], response: $response);
  1292.     }
  1293.     #[ParamConverter("city", converter: "city_converter")]
  1294.     #[Route("/api/profiles/listing/city/{city}/list-pages/services", name: "api.profile_listing.list_pages.services", methods: "GET", format: "json")]
  1295.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1296.     #[OA\Tag(name: "ProfileListing")]
  1297.     #[OA\Response(
  1298.         response: 200,
  1299.         description: 'Листинг анкет для страницы списка услуг',
  1300.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1301.     )]
  1302.     public function intimServices(Request $request, City $city): Response
  1303.     {
  1304.         $isApiRequest = $this->isApiRequest($request);
  1305.         $servicesByGroup = $this->serviceRepository->allIndexedByGroup();
  1306.         $profilesOnListPages = null;
  1307.         $specs = null;
  1308.         if ($this->features->profiles_on_list_pages()) {
  1309.             $specs = $this->profileListSpecificationService->listPagesWithProvidedServices();
  1310.             $profilesOnListPages = $this->paginatedListing(
  1311.                 $city,
  1312.                 '/city/{city}/list-pages/services',
  1313.                 ['city' => $city->getId()],
  1314.                 $specs->spec(),
  1315.                 fallbackWithinCity: false
  1316.             );
  1317.         } elseif ($isApiRequest) {
  1318.             throw $this->createNotFoundException();
  1319.         }
  1320.         if ($isApiRequest) {
  1321.             return $this->jsonListingResponse($profilesOnListPages, $city, $specs);
  1322.         }
  1323.         return $this->render('ProfileList/intim_services.html.twig', [
  1324.             'city' => $city,
  1325.             'servicesByGroup' => $servicesByGroup,
  1326.             'profiles' => $profilesOnListPages,
  1327.             'profiles_on_list_pages' => $profilesOnListPages,
  1328.             'skipSetCurrentListingPage' => true,
  1329.         ]);
  1330.     }
  1331.     /**
  1332.      * @Feature("extra_category_outcall")
  1333.      */
  1334.     #[ParamConverter("city", converter: "city_converter")]
  1335.     #[Route("/api/profiles/listing/city/{city}/category/outcall", name: "api.profile_listing.category.outcall", methods: "GET", format: "json")]
  1336.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1337.     #[OA\Tag(name: "ProfileListing")]
  1338.     #[OA\Response(
  1339.         response: 200,
  1340.         description: '',
  1341.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1342.     )]
  1343.     public function listOutcall(Request $request, City $city): Response
  1344.     {
  1345.         $specs = $this->profileListSpecificationService->listByOutcall();
  1346.         $response = new Response();
  1347.         $result = $this->paginatedListing($city, '/city/{city}/category/outcall', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1348.         if ($this->isApiRequest($request)) {
  1349.             return $this->jsonListingResponse($result, $city, $specs);
  1350.         }
  1351.         return $this->render('ProfileList/list.html.twig', [
  1352.             'profiles' => $result,
  1353.             'source' => $this->source,
  1354.             'source_default' => 'outcall',
  1355.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1356.                 'city' => $city->getUriIdentity(),
  1357.                 'page' => $this->getCurrentPageNumber(),
  1358.             ]),
  1359.             'recommendationSpec' => $specs->recommendationSpec(),
  1360.         ], response: $response);
  1361.     }
  1362.     /**
  1363.      * @Feature("extra_category_dwarfs")
  1364.      */
  1365.     #[ParamConverter("city", converter: "city_converter")]
  1366.     #[Route("/api/profiles/listing/city/{city}/category/dwarfs", name: "api.profile_listing.category.dwarfs", methods: "GET", format: "json")]
  1367.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1368.     #[OA\Tag(name: "ProfileListing")]
  1369.     #[OA\Response(
  1370.         response: 200,
  1371.         description: '',
  1372.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1373.     )]
  1374.     public function listDwarfs(Request $request, City $city): Response
  1375.     {
  1376.         $specs = $this->profileListSpecificationService->listDwarfs();
  1377.         $response = new Response();
  1378.         $result = $this->paginatedListing($city, '/city/{city}/category/dwarfs', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1379.         if ($this->isApiRequest($request)) {
  1380.             return $this->jsonListingResponse($result, $city, $specs);
  1381.         }
  1382.         return $this->render('ProfileList/list.html.twig', [
  1383.             'profiles' => $result,
  1384.             'source' => $this->source,
  1385.             'source_default' => 'dwarfs',
  1386.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1387.                 'city' => $city->getUriIdentity(),
  1388.                 'page' => $this->getCurrentPageNumber(),
  1389.             ]),
  1390.             'recommendationSpec' => $specs->recommendationSpec(),
  1391.         ], response: $response);
  1392.     }
  1393.     #[ParamConverter("city", converter: "city_converter")]
  1394.     #[Route("/api/profiles/listing/city/{city}/with_selfie", name: "api.profile_listing.with_selfie", methods: "GET", format: "json")]
  1395.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1396.     #[OA\Tag(name: "ProfileListing")]
  1397.     #[OA\Response(
  1398.         response: 200,
  1399.         description: '',
  1400.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1401.     )]
  1402.     public function listWithSelfie(Request $request, City $city): Response
  1403.     {
  1404.         $specs = $this->profileListSpecificationService->listWithSelfie();
  1405.         $response = new Response();
  1406.         $result = $this->paginatedListing($city, '/city/{city}/with_selfie', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  1407.         if ($this->isApiRequest($request)) {
  1408.             return $this->jsonListingResponse($result, $city, $specs);
  1409.         }
  1410.         return $this->render('ProfileList/list.html.twig', [
  1411.             'profiles' => $result,
  1412.             'source' => $this->source,
  1413.             'source_default' => self::RESULT_SOURCE_WITH_SELFIE,
  1414.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1415.                 'city' => $city->getUriIdentity(),
  1416.                 'page' => $this->getCurrentPageNumber()
  1417.             ]),
  1418.             'recommendationSpec' => $specs->recommendationSpec(),
  1419.         ], response: $response);
  1420.     }
  1421.     /**
  1422.      * @Feature("extra_category_top_100")
  1423.      */
  1424.     #[ParamConverter("city", converter: "city_converter")]
  1425.     #[Route("/api/profiles/listing/city/{city}/category/top_100", name: "api.profile_listing.category.top_100", methods: "GET", format: "json")]
  1426.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1427.     #[OA\Tag(name: "ProfileListing")]
  1428.     #[OA\Response(
  1429.         response: 200,
  1430.         description: '',
  1431.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1432.     )]
  1433.     public function listTop100(Request $request, City $city): Response
  1434.     {
  1435.         $specs = $this->profileListSpecificationService->listApproved();
  1436.         $result = $this->top100ProfilesService->getSortedProfilesByVisits($city);
  1437.         if ($this->isApiRequest($request)) {
  1438.             return $this->jsonListingResponse($result, $city, $specs);
  1439.         }
  1440.         return $this->render('ProfileList/list.html.twig', [
  1441.             'profiles' => $result,
  1442.             'source' => self::RESULT_SOURCE_TOP_100,
  1443.             'source_default' => self::RESULT_SOURCE_TOP_100,
  1444.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1445.                 'city' => $city->getUriIdentity(),
  1446.                 'page' => $this->getCurrentPageNumber(),
  1447.             ]),
  1448.             'recommendationSpec' => $specs->recommendationSpec(),
  1449.         ]);
  1450.     }
  1451.     /**
  1452.      * @Feature("extra_category_eromassage")
  1453.      */
  1454.     #[ParamConverter("city", converter: "city_converter")]
  1455.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  1456.     #[Route("/api/profiles/listing/city/{city}/county/{county}/eromassage", name: "api.profile_listing.by_county_eromassage", methods: "GET", format: "json")]
  1457.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1458.     #[OA\Tag(name: "ProfileListing")]
  1459.     #[OA\Response(
  1460.         response: 200,
  1461.         description: '',
  1462.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1463.     )]
  1464.     public function listByCountyEromassage(Request $request, City $city, County $county): Response
  1465.     {
  1466.         if (!$city->hasCounty($county)) {
  1467.             throw $this->createNotFoundException();
  1468.         }
  1469.         $specs = $this->profileListSpecificationService->listByCountyEromassage($county);
  1470.         $response = new Response();
  1471.         $result = $this->paginatedListing(
  1472.             $city,
  1473.             '/city/{city}/county/{county}/eromassage',
  1474.             ['city' => $city->getId(), 'county' => $county->getId()],
  1475.             $specs->spec(),
  1476.             null,
  1477.             null,
  1478.             $response
  1479.         );
  1480.         if ($this->isApiRequest($request)) {
  1481.             return $this->jsonListingResponse($result, $city, $specs);
  1482.         }
  1483.         $request->attributes->set('profiles_count', $result->totalCount());
  1484.         return $this->render('ProfileList/list.html.twig', [
  1485.             'profiles' => $result,
  1486.             'source' => $this->source,
  1487.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  1488.             'county' => $county,
  1489.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1490.                 'city' => $city->getUriIdentity(),
  1491.                 'county' => $county->getUriIdentity(),
  1492.                 'page' => $this->getCurrentPageNumber(),
  1493.             ]),
  1494.             'recommendationSpec' => $specs->recommendationSpec(),
  1495.         ], response: $response);
  1496.     }
  1497.     /**
  1498.      * @Feature("extra_category_eromassage")
  1499.      */
  1500.     #[ParamConverter("city", converter: "city_converter")]
  1501.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  1502.     #[Route("/api/profiles/listing/city/{city}/district/{district}/eromassage", name: "api.profile_listing.by_district_eromassage", methods: "GET", format: "json")]
  1503.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1504.     #[OA\Tag(name: "ProfileListing")]
  1505.     #[OA\Response(
  1506.         response: 200,
  1507.         description: '',
  1508.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1509.     )]
  1510.     public function listByDistrictEromassage(Request $request, City $city, District $district): Response
  1511.     {
  1512.         if (!$city->hasDistrict($district)) {
  1513.             throw $this->createNotFoundException();
  1514.         }
  1515.         $specs = $this->profileListSpecificationService->listByDistrictEromassage($district);
  1516.         $response = new Response();
  1517.         $result = $this->paginatedListing(
  1518.             $city,
  1519.             '/city/{city}/district/{district}/eromassage',
  1520.             ['city' => $city->getId(), 'district' => $district->getId()],
  1521.             $specs->spec(),
  1522.             null,
  1523.             null,
  1524.             $response
  1525.         );
  1526.         if ($this->isApiRequest($request)) {
  1527.             return $this->jsonListingResponse($result, $city, $specs);
  1528.         }
  1529.         $request->attributes->set('profiles_count', $result->totalCount());
  1530.         return $this->render('ProfileList/list.html.twig', [
  1531.             'profiles' => $result,
  1532.             'source' => $this->source,
  1533.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  1534.             'district' => $district,
  1535.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1536.                 'city' => $city->getUriIdentity(),
  1537.                 'district' => $district->getUriIdentity(),
  1538.                 'page' => $this->getCurrentPageNumber(),
  1539.             ]),
  1540.             'recommendationSpec' => $specs->recommendationSpec(),
  1541.         ], response: $response);
  1542.     }
  1543.     /**
  1544.      * @Feature("extra_category_eromassage")
  1545.      */
  1546.     #[ParamConverter("city", converter: "city_converter")]
  1547.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  1548.     #[Route("/api/profiles/listing/city/{city}/station/{station}/eromassage", name: "api.profile_listing.by_station_eromassage", methods: "GET", format: "json")]
  1549.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1550.     #[OA\Tag(name: "ProfileListing")]
  1551.     #[OA\Response(
  1552.         response: 200,
  1553.         description: '',
  1554.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1555.     )]
  1556.     public function listByStationEromassage(Request $request, City $city, Station $station): Response
  1557.     {
  1558.         if (!$city->hasStation($station)) {
  1559.             throw $this->createNotFoundException();
  1560.         }
  1561.         $specs = $this->profileListSpecificationService->listByStationEromassage($station);
  1562.         $response = new Response();
  1563.         $result = $this->paginatedListing(
  1564.             $city,
  1565.             '/city/{city}/station/{station}/eromassage',
  1566.             ['city' => $city->getId(), 'station' => $station->getId()],
  1567.             $specs->spec(),
  1568.             null,
  1569.             null,
  1570.             $response
  1571.         );
  1572.         if ($this->isApiRequest($request)) {
  1573.             return $this->jsonListingResponse($result, $city, $specs);
  1574.         }
  1575.         $request->attributes->set('profiles_count', $result->totalCount());
  1576.         return $this->render('ProfileList/list.html.twig', [
  1577.             'profiles' => $result,
  1578.             'source' => $this->source,
  1579.             'source_default' => self::RESULT_SOURCE_EROMASSAGE,
  1580.             'station' => $station,
  1581.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1582.                 'city' => $city->getUriIdentity(),
  1583.                 'station' => $station->getUriIdentity(),
  1584.                 'page' => $this->getCurrentPageNumber(),
  1585.             ]),
  1586.             'recommendationSpec' => $specs->recommendationSpec(),
  1587.         ], response: $response);
  1588.     }
  1589.     /**
  1590.      * @Feature("extra_category_verified")
  1591.      */
  1592.     #[ParamConverter("city", converter: "city_converter")]
  1593.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  1594.     #[Route("/api/profiles/listing/city/{city}/county/{county}/proverennye", name: "api.profile_listing.by_county_verified", methods: "GET", format: "json")]
  1595.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1596.     #[OA\Tag(name: "ProfileListing")]
  1597.     #[OA\Response(
  1598.         response: 200,
  1599.         description: '',
  1600.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1601.     )]
  1602.     public function listByCountyVerified(Request $request, City $city, County $county): Response
  1603.     {
  1604.         if (!$city->hasCounty($county)) {
  1605.             throw $this->createNotFoundException();
  1606.         }
  1607.         $specs = $this->profileListSpecificationService->listByCountyVerified($county);
  1608.         $response = new Response();
  1609.         $result = $this->paginatedListing(
  1610.             $city,
  1611.             '/city/{city}/county/{county}/proverennye',
  1612.             ['city' => $city->getId(), 'county' => $county->getId()],
  1613.             $specs->spec(),
  1614.             null,
  1615.             null,
  1616.             $response
  1617.         );
  1618.         if ($this->isApiRequest($request)) {
  1619.             return $this->jsonListingResponse($result, $city, $specs);
  1620.         }
  1621.         $request->attributes->set('profiles_count', $result->totalCount());
  1622.         return $this->render('ProfileList/list.html.twig', [
  1623.             'profiles' => $result,
  1624.             'source' => $this->source,
  1625.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1626.             'county' => $county,
  1627.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1628.                 'city' => $city->getUriIdentity(),
  1629.                 'county' => $county->getUriIdentity(),
  1630.                 'page' => $this->getCurrentPageNumber(),
  1631.             ]),
  1632.             'recommendationSpec' => $specs->recommendationSpec(),
  1633.         ], response: $response);
  1634.     }
  1635.     /**
  1636.      * @Feature("extra_category_verified")
  1637.      */
  1638.     #[ParamConverter("city", converter: "city_converter")]
  1639.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  1640.     #[Route("/api/profiles/listing/city/{city}/district/{district}/proverennye", name: "api.profile_listing.by_district_verified", methods: "GET", format: "json")]
  1641.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1642.     #[OA\Tag(name: "ProfileListing")]
  1643.     #[OA\Response(
  1644.         response: 200,
  1645.         description: '',
  1646.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1647.     )]
  1648.     public function listByDistrictVerified(Request $request, City $city, District $district): Response
  1649.     {
  1650.         if (!$city->hasDistrict($district)) {
  1651.             throw $this->createNotFoundException();
  1652.         }
  1653.         $specs = $this->profileListSpecificationService->listByDistrictVerified($district);
  1654.         $response = new Response();
  1655.         $result = $this->paginatedListing(
  1656.             $city,
  1657.             '/city/{city}/district/{district}/proverennye',
  1658.             ['city' => $city->getId(), 'district' => $district->getId()],
  1659.             $specs->spec(),
  1660.             null,
  1661.             null,
  1662.             $response
  1663.         );
  1664.         if ($this->isApiRequest($request)) {
  1665.             return $this->jsonListingResponse($result, $city, $specs);
  1666.         }
  1667.         $request->attributes->set('profiles_count', $result->totalCount());
  1668.         return $this->render('ProfileList/list.html.twig', [
  1669.             'profiles' => $result,
  1670.             'source' => $this->source,
  1671.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1672.             'district' => $district,
  1673.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1674.                 'city' => $city->getUriIdentity(),
  1675.                 'district' => $district->getUriIdentity(),
  1676.                 'page' => $this->getCurrentPageNumber(),
  1677.             ]),
  1678.             'recommendationSpec' => $specs->recommendationSpec(),
  1679.         ], response: $response);
  1680.     }
  1681.     /**
  1682.      * @Feature("extra_category_verified")
  1683.      */
  1684.     #[ParamConverter("city", converter: "city_converter")]
  1685.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  1686.     #[Route("/api/profiles/listing/city/{city}/station/{station}/proverennye", name: "api.profile_listing.by_station_verified", methods: "GET", format: "json")]
  1687.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1688.     #[OA\Tag(name: "ProfileListing")]
  1689.     #[OA\Response(
  1690.         response: 200,
  1691.         description: '',
  1692.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1693.     )]
  1694.     public function listByStationVerified(Request $request, City $city, Station $station): Response
  1695.     {
  1696.         if (!$city->hasStation($station)) {
  1697.             throw $this->createNotFoundException();
  1698.         }
  1699.         $specs = $this->profileListSpecificationService->listByStationVerified($station);
  1700.         $response = new Response();
  1701.         $result = $this->paginatedListing(
  1702.             $city,
  1703.             '/city/{city}/station/{station}/proverennye',
  1704.             ['city' => $city->getId(), 'station' => $station->getId()],
  1705.             $specs->spec(),
  1706.             null,
  1707.             null,
  1708.             $response
  1709.         );
  1710.         if ($this->isApiRequest($request)) {
  1711.             return $this->jsonListingResponse($result, $city, $specs);
  1712.         }
  1713.         $request->attributes->set('profiles_count', $result->totalCount());
  1714.         return $this->render('ProfileList/list.html.twig', [
  1715.             'profiles' => $result,
  1716.             'source' => $this->source,
  1717.             'source_default' => self::RESULT_SOURCE_VERIFIED,
  1718.             'station' => $station,
  1719.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1720.                 'city' => $city->getUriIdentity(),
  1721.                 'station' => $station->getUriIdentity(),
  1722.                 'page' => $this->getCurrentPageNumber(),
  1723.             ]),
  1724.             'recommendationSpec' => $specs->recommendationSpec(),
  1725.         ], response: $response);
  1726.     }
  1727.     /**
  1728.      * @Feature("extra_category_cheap")
  1729.      */
  1730.     #[ParamConverter("city", converter: "city_converter")]
  1731.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  1732.     #[Route("/api/profiles/listing/city/{city}/county/{county}/deshevye", name: "api.profile_listing.by_county_cheap", methods: "GET", format: "json")]
  1733.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1734.     #[OA\Tag(name: "ProfileListing")]
  1735.     #[OA\Response(
  1736.         response: 200,
  1737.         description: '',
  1738.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1739.     )]
  1740.     public function listByCountyCheap(Request $request, City $city, County $county): Response
  1741.     {
  1742.         if (!$city->hasCounty($county)) {
  1743.             throw $this->createNotFoundException();
  1744.         }
  1745.         $specs = $this->profileListSpecificationService->listByCountyCheap($county);
  1746.         $response = new Response();
  1747.         $result = $this->paginatedListing(
  1748.             $city,
  1749.             null,
  1750.             [],
  1751.             $specs->spec(),
  1752.             null,
  1753.             null,
  1754.             $response
  1755.         );
  1756.         if ($this->isApiRequest($request)) {
  1757.             return $this->jsonListingResponse($result, $city, $specs);
  1758.         }
  1759.         $request->attributes->set('profiles_count', $result->totalCount());
  1760.         return $this->render('ProfileList/list.html.twig', [
  1761.             'profiles' => $result,
  1762.             'source' => $this->source,
  1763.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1764.             'county' => $county,
  1765.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1766.                 'city' => $city->getUriIdentity(),
  1767.                 'county' => $county->getUriIdentity(),
  1768.                 'page' => $this->getCurrentPageNumber(),
  1769.             ]),
  1770.             'recommendationSpec' => $specs->recommendationSpec(),
  1771.         ], response: $response);
  1772.     }
  1773.     /**
  1774.      * @Feature("extra_category_cheap")
  1775.      */
  1776.     #[ParamConverter("city", converter: "city_converter")]
  1777.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  1778.     #[Route("/api/profiles/listing/city/{city}/district/{district}/deshevye", name: "api.profile_listing.by_district_cheap", methods: "GET", format: "json")]
  1779.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1780.     #[OA\Tag(name: "ProfileListing")]
  1781.     #[OA\Response(
  1782.         response: 200,
  1783.         description: '',
  1784.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1785.     )]
  1786.     public function listByDistrictCheap(Request $request, City $city, District $district): Response
  1787.     {
  1788.         if (!$city->hasDistrict($district)) {
  1789.             throw $this->createNotFoundException();
  1790.         }
  1791.         $specs = $this->profileListSpecificationService->listByDistrictCheap($district);
  1792.         $response = new Response();
  1793.         $result = $this->paginatedListing(
  1794.             $city,
  1795.             null,
  1796.             [],
  1797.             $specs->spec(),
  1798.             null,
  1799.             null,
  1800.             $response
  1801.         );
  1802.         if ($this->isApiRequest($request)) {
  1803.             return $this->jsonListingResponse($result, $city, $specs);
  1804.         }
  1805.         $request->attributes->set('profiles_count', $result->totalCount());
  1806.         return $this->render('ProfileList/list.html.twig', [
  1807.             'profiles' => $result,
  1808.             'source' => $this->source,
  1809.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1810.             'district' => $district,
  1811.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1812.                 'city' => $city->getUriIdentity(),
  1813.                 'district' => $district->getUriIdentity(),
  1814.                 'page' => $this->getCurrentPageNumber(),
  1815.             ]),
  1816.             'recommendationSpec' => $specs->recommendationSpec(),
  1817.         ], response: $response);
  1818.     }
  1819.     /**
  1820.      * @Feature("extra_category_cheap")
  1821.      */
  1822.     #[ParamConverter("city", converter: "city_converter")]
  1823.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  1824.     #[Route("/api/profiles/listing/city/{city}/station/{station}/deshevye", name: "api.profile_listing.by_station_cheap", methods: "GET", format: "json")]
  1825.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1826.     #[OA\Tag(name: "ProfileListing")]
  1827.     #[OA\Response(
  1828.         response: 200,
  1829.         description: '',
  1830.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1831.     )]
  1832.     public function listByStationCheap(Request $request, City $city, Station $station): Response
  1833.     {
  1834.         if (!$city->hasStation($station)) {
  1835.             throw $this->createNotFoundException();
  1836.         }
  1837.         $specs = $this->profileListSpecificationService->listByStationCheap($station);
  1838.         $response = new Response();
  1839.         $result = $this->paginatedListing(
  1840.             $city,
  1841.             null,
  1842.             [],
  1843.             $specs->spec(),
  1844.             null,
  1845.             null,
  1846.             $response
  1847.         );
  1848.         if ($this->isApiRequest($request)) {
  1849.             return $this->jsonListingResponse($result, $city, $specs);
  1850.         }
  1851.         $request->attributes->set('profiles_count', $result->totalCount());
  1852.         return $this->render('ProfileList/list.html.twig', [
  1853.             'profiles' => $result,
  1854.             'source' => $this->source,
  1855.             'source_default' => self::RESULT_SOURCE_CHEAP,
  1856.             'station' => $station,
  1857.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1858.                 'city' => $city->getUriIdentity(),
  1859.                 'station' => $station->getUriIdentity(),
  1860.                 'page' => $this->getCurrentPageNumber(),
  1861.             ]),
  1862.             'recommendationSpec' => $specs->recommendationSpec(),
  1863.         ], response: $response);
  1864.     }
  1865.     /**
  1866.      * @Feature("extra_category_mature")
  1867.      */
  1868.     #[ParamConverter("city", converter: "city_converter")]
  1869.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  1870.     #[Route("/api/profiles/listing/city/{city}/county/{county}/zrelye", name: "api.profile_listing.by_county_mature", methods: "GET", format: "json")]
  1871.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1872.     #[OA\Tag(name: "ProfileListing")]
  1873.     #[OA\Response(
  1874.         response: 200,
  1875.         description: '',
  1876.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1877.     )]
  1878.     public function listByCountyMature(Request $request, City $city, County $county): Response
  1879.     {
  1880.         if (!$city->hasCounty($county)) {
  1881.             throw $this->createNotFoundException();
  1882.         }
  1883.         $specs = $this->profileListSpecificationService->listByCountyMature($county);
  1884.         $response = new Response();
  1885.         $result = $this->paginatedListing(
  1886.             $city,
  1887.             null,
  1888.             [],
  1889.             $specs->spec(),
  1890.             null,
  1891.             null,
  1892.             $response
  1893.         );
  1894.         if ($this->isApiRequest($request)) {
  1895.             return $this->jsonListingResponse($result, $city, $specs);
  1896.         }
  1897.         $request->attributes->set('profiles_count', $result->totalCount());
  1898.         return $this->render('ProfileList/list.html.twig', [
  1899.             'profiles' => $result,
  1900.             'source' => $this->source,
  1901.             'source_default' => self::RESULT_SOURCE_MATURE,
  1902.             'county' => $county,
  1903.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1904.                 'city' => $city->getUriIdentity(),
  1905.                 'county' => $county->getUriIdentity(),
  1906.                 'page' => $this->getCurrentPageNumber(),
  1907.             ]),
  1908.             'recommendationSpec' => $specs->recommendationSpec(),
  1909.         ], response: $response);
  1910.     }
  1911.     /**
  1912.      * @Feature("extra_category_mature")
  1913.      */
  1914.     #[ParamConverter("city", converter: "city_converter")]
  1915.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  1916.     #[Route("/api/profiles/listing/city/{city}/district/{district}/zrelye", name: "api.profile_listing.by_district_mature", methods: "GET", format: "json")]
  1917.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1918.     #[OA\Tag(name: "ProfileListing")]
  1919.     #[OA\Response(
  1920.         response: 200,
  1921.         description: '',
  1922.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1923.     )]
  1924.     public function listByDistrictMature(Request $request, City $city, District $district): Response
  1925.     {
  1926.         if (!$city->hasDistrict($district)) {
  1927.             throw $this->createNotFoundException();
  1928.         }
  1929.         $specs = $this->profileListSpecificationService->listByDistrictMature($district);
  1930.         $response = new Response();
  1931.         $result = $this->paginatedListing(
  1932.             $city,
  1933.             null,
  1934.             [],
  1935.             $specs->spec(),
  1936.             null,
  1937.             null,
  1938.             $response
  1939.         );
  1940.         if ($this->isApiRequest($request)) {
  1941.             return $this->jsonListingResponse($result, $city, $specs);
  1942.         }
  1943.         $request->attributes->set('profiles_count', $result->totalCount());
  1944.         return $this->render('ProfileList/list.html.twig', [
  1945.             'profiles' => $result,
  1946.             'source' => $this->source,
  1947.             'source_default' => self::RESULT_SOURCE_MATURE,
  1948.             'district' => $district,
  1949.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1950.                 'city' => $city->getUriIdentity(),
  1951.                 'district' => $district->getUriIdentity(),
  1952.                 'page' => $this->getCurrentPageNumber(),
  1953.             ]),
  1954.             'recommendationSpec' => $specs->recommendationSpec(),
  1955.         ], response: $response);
  1956.     }
  1957.     /**
  1958.      * @Feature("extra_category_mature")
  1959.      */
  1960.     #[ParamConverter("city", converter: "city_converter")]
  1961.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  1962.     #[Route("/api/profiles/listing/city/{city}/station/{station}/zrelye", name: "api.profile_listing.by_station_mature", methods: "GET", format: "json")]
  1963.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  1964.     #[OA\Tag(name: "ProfileListing")]
  1965.     #[OA\Response(
  1966.         response: 200,
  1967.         description: '',
  1968.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  1969.     )]
  1970.     public function listByStationMature(Request $request, City $city, Station $station): Response
  1971.     {
  1972.         if (!$city->hasStation($station)) {
  1973.             throw $this->createNotFoundException();
  1974.         }
  1975.         $specs = $this->profileListSpecificationService->listByStationMature($station);
  1976.         $response = new Response();
  1977.         $result = $this->paginatedListing(
  1978.             $city,
  1979.             null,
  1980.             [],
  1981.             $specs->spec(),
  1982.             null,
  1983.             null,
  1984.             $response
  1985.         );
  1986.         if ($this->isApiRequest($request)) {
  1987.             return $this->jsonListingResponse($result, $city, $specs);
  1988.         }
  1989.         $request->attributes->set('profiles_count', $result->totalCount());
  1990.         return $this->render('ProfileList/list.html.twig', [
  1991.             'profiles' => $result,
  1992.             'source' => $this->source,
  1993.             'source_default' => self::RESULT_SOURCE_MATURE,
  1994.             'station' => $station,
  1995.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  1996.                 'city' => $city->getUriIdentity(),
  1997.                 'station' => $station->getUriIdentity(),
  1998.                 'page' => $this->getCurrentPageNumber(),
  1999.             ]),
  2000.             'recommendationSpec' => $specs->recommendationSpec(),
  2001.         ], response: $response);
  2002.     }
  2003.     /**
  2004.      * @Feature("extra_category_uzbek")
  2005.      */
  2006.     #[ParamConverter("city", converter: "city_converter")]
  2007.     #[Entity("county", expr: "repository.ofUriIdentityWithinCity(county, city)")]
  2008.     #[Route("/api/profiles/listing/city/{city}/county/{county}/uzbechki", name: "api.profile_listing.by_county_uzbek", methods: "GET", format: "json")]
  2009.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2010.     #[OA\Tag(name: "ProfileListing")]
  2011.     #[OA\Response(
  2012.         response: 200,
  2013.         description: '',
  2014.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2015.     )]
  2016.     public function listByCountyUzbek(Request $request, City $city, County $county): Response
  2017.     {
  2018.         if (!$city->hasCounty($county)) {
  2019.             throw $this->createNotFoundException();
  2020.         }
  2021.         $specs = $this->profileListSpecificationService->listByCountyUzbek($county);
  2022.         $response = new Response();
  2023.         $result = $this->paginatedListing(
  2024.             $city,
  2025.             '/city/{city}/county/{county}/uzbechki',
  2026.             ['city' => $city->getId(), 'county' => $county->getId()],
  2027.             $specs->spec(),
  2028.             null,
  2029.             null,
  2030.             $response
  2031.         );
  2032.         if ($this->isApiRequest($request)) {
  2033.             return $this->jsonListingResponse($result, $city, $specs);
  2034.         }
  2035.         $request->attributes->set('profiles_count', $result->totalCount());
  2036.         return $this->render('ProfileList/list.html.twig', [
  2037.             'profiles' => $result,
  2038.             'source' => $this->source,
  2039.             'source_default' => self::RESULT_SOURCE_UZBEK,
  2040.             'county' => $county,
  2041.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2042.                 'city' => $city->getUriIdentity(),
  2043.                 'county' => $county->getUriIdentity(),
  2044.                 'page' => $this->getCurrentPageNumber(),
  2045.             ]),
  2046.             'recommendationSpec' => $specs->recommendationSpec(),
  2047.         ], response: $response);
  2048.     }
  2049.     /**
  2050.      * @Feature("extra_category_uzbek")
  2051.      */
  2052.     #[ParamConverter("city", converter: "city_converter")]
  2053.     #[Entity("district", expr: "repository.ofUriIdentityWithinCity(district, city)")]
  2054.     #[Route("/api/profiles/listing/city/{city}/district/{district}/uzbechki", name: "api.profile_listing.by_district_uzbek", methods: "GET", format: "json")]
  2055.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2056.     #[OA\Tag(name: "ProfileListing")]
  2057.     #[OA\Response(
  2058.         response: 200,
  2059.         description: '',
  2060.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2061.     )]
  2062.     public function listByDistrictUzbek(Request $request, City $city, District $district): Response
  2063.     {
  2064.         if (!$city->hasDistrict($district)) {
  2065.             throw $this->createNotFoundException();
  2066.         }
  2067.         $specs = $this->profileListSpecificationService->listByDistrictUzbek($district);
  2068.         $response = new Response();
  2069.         $result = $this->paginatedListing(
  2070.             $city,
  2071.             '/city/{city}/district/{district}/uzbechki',
  2072.             ['city' => $city->getId(), 'district' => $district->getId()],
  2073.             $specs->spec(),
  2074.             null,
  2075.             null,
  2076.             $response
  2077.         );
  2078.         if ($this->isApiRequest($request)) {
  2079.             return $this->jsonListingResponse($result, $city, $specs);
  2080.         }
  2081.         $request->attributes->set('profiles_count', $result->totalCount());
  2082.         return $this->render('ProfileList/list.html.twig', [
  2083.             'profiles' => $result,
  2084.             'source' => $this->source,
  2085.             'source_default' => self::RESULT_SOURCE_UZBEK,
  2086.             'district' => $district,
  2087.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2088.                 'city' => $city->getUriIdentity(),
  2089.                 'district' => $district->getUriIdentity(),
  2090.                 'page' => $this->getCurrentPageNumber(),
  2091.             ]),
  2092.             'recommendationSpec' => $specs->recommendationSpec(),
  2093.         ], response: $response);
  2094.     }
  2095.     /**
  2096.      * @Feature("extra_category_uzbek")
  2097.      */
  2098.     #[ParamConverter("city", converter: "city_converter")]
  2099.     #[Entity("station", expr: "repository.ofUriIdentityWithinCity(station, city)")]
  2100.     #[Route("/api/profiles/listing/city/{city}/station/{station}/uzbechki", name: "api.profile_listing.by_station_uzbek", methods: "GET", format: "json")]
  2101.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2102.     #[OA\Tag(name: "ProfileListing")]
  2103.     #[OA\Response(
  2104.         response: 200,
  2105.         description: '',
  2106.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2107.     )]
  2108.     public function listByStationUzbek(Request $request, City $city, Station $station): Response
  2109.     {
  2110.         if (!$city->hasStation($station)) {
  2111.             throw $this->createNotFoundException();
  2112.         }
  2113.         $specs = $this->profileListSpecificationService->listByStationUzbek($station);
  2114.         $response = new Response();
  2115.         $result = $this->paginatedListing(
  2116.             $city,
  2117.             '/city/{city}/station/{station}/uzbechki',
  2118.             ['city' => $city->getId(), 'station' => $station->getId()],
  2119.             $specs->spec(),
  2120.             null,
  2121.             null,
  2122.             $response
  2123.         );
  2124.         if ($this->isApiRequest($request)) {
  2125.             return $this->jsonListingResponse($result, $city, $specs);
  2126.         }
  2127.         $request->attributes->set('profiles_count', $result->totalCount());
  2128.         return $this->render('ProfileList/list.html.twig', [
  2129.             'profiles' => $result,
  2130.             'source' => $this->source,
  2131.             'source_default' => self::RESULT_SOURCE_UZBEK,
  2132.             'station' => $station,
  2133.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2134.                 'city' => $city->getUriIdentity(),
  2135.                 'station' => $station->getUriIdentity(),
  2136.                 'page' => $this->getCurrentPageNumber(),
  2137.             ]),
  2138.             'recommendationSpec' => $specs->recommendationSpec(),
  2139.         ], response: $response);
  2140.     }
  2141.     #[ParamConverter("city", converter: "city_converter")]
  2142.     #[Route("/api/profiles/listing/city/{city}/price/{priceType}", name: "api.profile_listing.by_price", methods: "GET", format: "json")]
  2143.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2144.     #[OA\Parameter(name: "minPrice", description: "Min price for price type 'range'. 0 to omit.", in: "query", schema: new OA\Schema(type: "integer", default: 0, minimum: 0))]
  2145.     #[OA\Parameter(name: "maxPrice", description: "Max price for price types 'range' and 'less_than'. 0 to omit.", in: "query", schema: new OA\Schema(type: "integer", default: 0, minimum: 0))]
  2146.     #[OA\Tag(name: "ProfileListing")]
  2147.     #[OA\Response(
  2148.         response: 200,
  2149.         description: '',
  2150.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2151.     )]
  2152.     public function listByPrice(Request $request, CountryCurrencyResolver $countryCurrencyResolver, City $city, string $priceType, int $minPrice = null, int $maxPrice = null): Response
  2153.     {
  2154.         if ($this->isApiRequest($request) && in_array($priceType, ['range', 'less_than'], true)) {
  2155.             $minPrice = $request->query->getInt('minPrice');
  2156.             $maxPrice = $request->query->getInt('maxPrice');
  2157.             if ('range' === $priceType && 0 === $minPrice && 0 === $maxPrice) {
  2158.                 throw new \InvalidArgumentException('Either minPrice or maxPrice is required for price type "range".');
  2159.             }
  2160.             if ('less_than' === $priceType && 0 === $maxPrice) {
  2161.                 throw new \InvalidArgumentException('MaxPrice is required for price type "less_than".');
  2162.             }
  2163.         }
  2164.         $specs = $this->profileListSpecificationService->listByPrice($city, $priceType, $minPrice, $maxPrice);
  2165.         $response = new Response();
  2166.         $apiEndpoint = match ($priceType) {
  2167.             'low', 'high', 'elite' => '/city/{city}/price/'.$priceType,
  2168.             'less_than' => '/city/{city}/price/less_than/{maxPrice}',
  2169.             default => null,
  2170.         };
  2171.         $result = $this->paginatedListing($city, $apiEndpoint, [
  2172.             'city' => $city->getId(),
  2173.             'maxPrice' => $maxPrice,
  2174.         ], $specs->spec(), null, null, $response);
  2175.         if ($this->isApiRequest($request)) {
  2176.             return $this->jsonListingResponse($result, $city, $specs);
  2177.         }
  2178.         $request->attributes->set('profiles_count', $result->totalCount());
  2179.         return $this->render('ProfileList/list.html.twig', [
  2180.             'profiles' => $result,
  2181.             'source' => $this->source,
  2182.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2183.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2184.                 'city' => $city->getUriIdentity(),
  2185.                 'priceType' => $priceType,
  2186.                 'minPrice' => $minPrice,
  2187.                 'maxPrice' => $maxPrice,
  2188.                 'page' => $this->getCurrentPageNumber()
  2189.             ]),
  2190.             'recommendationSpec' => 'less_than' === $priceType ? null : $specs->recommendationSpec(),
  2191.             'skip_listing_recommendations' => 'less_than' === $priceType,
  2192.         ], response: $response);
  2193.     }
  2194.     #[ParamConverter("city", converter: "city_converter")]
  2195.     #[Route("/api/profiles/listing/city/{city}/age/{ageType}", name: "api.profile_listing.by_age", methods: "GET", format: "json")]
  2196.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2197.     #[OA\Tag(name: "ProfileListing")]
  2198.     #[OA\Response(
  2199.         response: 200,
  2200.         description: '',
  2201.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2202.     )]
  2203.     public function listByAge(Request $request, City $city, string $ageType, int $minAge = null, int $maxAge = null): Response
  2204.     {
  2205.         $specs = $this->profileListSpecificationService->listByAge($ageType, $minAge, $maxAge);
  2206.         $response = new Response();
  2207.         $apiEndpoint = in_array($ageType, ['young', 'old']) ? '/city/{city}/age/'.$ageType : null;
  2208.         $result = $this->paginatedListing($city, $apiEndpoint, ['city' => $city->getId()], $specs->spec(), null, null, $response);
  2209.         if ($this->isApiRequest($request)) {
  2210.             return $this->jsonListingResponse($result, $city, $specs);
  2211.         }
  2212.         return $this->render('ProfileList/list.html.twig', [
  2213.             'profiles' => $result,
  2214.             'source' => $this->source,
  2215.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2216.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2217.                 'city' => $city->getUriIdentity(),
  2218.                 'ageType' => $ageType,
  2219.                 'minAge' => $minAge,
  2220.                 'maxAge' => $maxAge,
  2221.                 'page' => $this->getCurrentPageNumber()
  2222.             ]),
  2223.             'recommendationSpec' => $specs->recommendationSpec(),
  2224.         ], response: $response);
  2225.     }
  2226.     #[ParamConverter("city", converter: "city_converter")]
  2227.     #[Route("/api/profiles/listing/city/{city}/height/{heightType}", name: "api.profile_listing.by_height", methods: "GET", format: "json")]
  2228.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2229.     #[OA\Tag(name: "ProfileListing")]
  2230.     #[OA\Response(
  2231.         response: 200,
  2232.         description: '',
  2233.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2234.     )]
  2235.     public function listByHeight(Request $request, City $city, string $heightType): Response
  2236.     {
  2237.         $specs = $this->profileListSpecificationService->listByHeight($heightType);
  2238.         $response = new Response();
  2239.         $result = $this->paginatedListing($city, '/city/{city}/height/'.$heightType, ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2240.         if ($this->isApiRequest($request)) {
  2241.             return $this->jsonListingResponse($result, $city, $specs);
  2242.         }
  2243.         return $this->render('ProfileList/list.html.twig', [
  2244.             'profiles' => $result,
  2245.             'source' => $this->source,
  2246.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2247.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2248.                 'city' => $city->getUriIdentity(),
  2249.                 'heightType' => $heightType,
  2250.                 'page' => $this->getCurrentPageNumber()
  2251.             ]),
  2252.             'recommendationSpec' => $specs->recommendationSpec(),
  2253.         ], response: $response);
  2254.     }
  2255.     #[ParamConverter("city", converter: "city_converter")]
  2256.     #[Route("/api/profiles/listing/city/{city}/breasttype/{breastType}", name: "api.profile_listing.by_breast_type", methods: "GET", format: "json")]
  2257.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2258.     #[OA\Tag(name: "ProfileListing")]
  2259.     #[OA\Response(
  2260.         response: 200,
  2261.         description: '',
  2262.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2263.     )]
  2264.     public function listByBreastType(Request $request, City $city, string $breastType): Response
  2265.     {
  2266.         if (null === $type = BreastTypes::getValueByUriIdentity($breastType)) {
  2267.             throw $this->createNotFoundException();
  2268.         }
  2269.         $specs = $this->profileListSpecificationService->listByBreastType($breastType);
  2270.         $response = new Response();
  2271.         $alternativeSpec = $this->getORSpecForItemsArray(BreastTypes::getList(), function($item): ProfileWithBreastType {
  2272.             return new ProfileWithBreastType($item);
  2273.         });
  2274.         $result = $this->paginatedListing($city, '/city/{city}/breasttype/'.$type, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2275.         if ($this->isApiRequest($request)) {
  2276.             return $this->jsonListingResponse($result, $city, $specs);
  2277.         }
  2278.         return $this->render('ProfileList/list.html.twig', [
  2279.             'profiles' => $result,
  2280.             'source' => $this->source,
  2281.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2282.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2283.                 'city' => $city->getUriIdentity(),
  2284.                 'breastType' => $breastType,
  2285.                 'page' => $this->getCurrentPageNumber()
  2286.             ]),
  2287.             'recommendationSpec' => $specs->recommendationSpec(),
  2288.         ], response: $response);
  2289.     }
  2290.     #[ParamConverter("city", converter: "city_converter")]
  2291.     #[Route("/api/profiles/listing/city/{city}/haircolor/{hairColor}", name: "api.profile_listing.by_haircolor", methods: "GET", format: "json")]
  2292.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2293.     #[OA\Tag(name: "ProfileListing")]
  2294.     #[OA\Response(
  2295.         response: 200,
  2296.         description: '',
  2297.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2298.     )]
  2299.     public function listByHairColor(Request $request, City $city, string $hairColor): Response
  2300.     {
  2301.         if (null === $color = HairColors::getValueByUriIdentity($hairColor)) {
  2302.             throw $this->createNotFoundException();
  2303.         }
  2304.         $specs = $this->profileListSpecificationService->listByHairColor($hairColor);
  2305.         $response = new Response();
  2306.         $alternativeSpec = $this->getORSpecForItemsArray(HairColors::getList(), function($item): ProfileWithHairColor {
  2307.             return new ProfileWithHairColor($item);
  2308.         });
  2309.         $result = $this->paginatedListing($city, '/city/{city}/haircolor/'.$color, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2310.         if ($this->isApiRequest($request)) {
  2311.             return $this->jsonListingResponse($result, $city, $specs);
  2312.         }
  2313.         return $this->render('ProfileList/list.html.twig', [
  2314.             'profiles' => $result,
  2315.             'source' => $this->source,
  2316.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2317.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2318.                 'city' => $city->getUriIdentity(),
  2319.                 'hairColor' => $hairColor,
  2320.                 'page' => $this->getCurrentPageNumber()
  2321.             ]),
  2322.             'recommendationSpec' => $specs->recommendationSpec(),
  2323.         ], response: $response);
  2324.     }
  2325.     #[ParamConverter("city", converter: "city_converter")]
  2326.     #[Route("/api/profiles/listing/city/{city}/bodytype/{bodyType}", name: "api.profile_listing.by_bodytype", methods: "GET", format: "json")]
  2327.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2328.     #[OA\Tag(name: "ProfileListing")]
  2329.     #[OA\Response(
  2330.         response: 200,
  2331.         description: '',
  2332.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2333.     )]
  2334.     public function listByBodyType(Request $request, City $city, string $bodyType): Response
  2335.     {
  2336.         if (null === $type = BodyTypes::getValueByUriIdentity($bodyType)) {
  2337.             throw $this->createNotFoundException();
  2338.         }
  2339.         $specs = $this->profileListSpecificationService->listByBodyType($bodyType);
  2340.         $response = new Response();
  2341.         $alternativeSpec = $this->getORSpecForItemsArray(BodyTypes::getList(), function($item): ProfileWithBodyType {
  2342.             return new ProfileWithBodyType($item);
  2343.         });
  2344.         $result = $this->paginatedListing($city, '/city/{city}/bodytype/'.$type, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2345.         if ($this->isApiRequest($request)) {
  2346.             return $this->jsonListingResponse($result, $city, $specs);
  2347.         }
  2348.         return $this->render('ProfileList/list.html.twig', [
  2349.             'profiles' => $result,
  2350.             'source' => $this->source,
  2351.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2352.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2353.                 'city' => $city->getUriIdentity(),
  2354.                 'bodyType' => $bodyType,
  2355.                 'page' => $this->getCurrentPageNumber()
  2356.             ]),
  2357.             'recommendationSpec' => $specs->recommendationSpec(),
  2358.         ], response: $response);
  2359.     }
  2360.     #[ParamConverter("city", converter: "city_converter")]
  2361.     #[Route("/api/profiles/listing/city/{city}/place/{placeType}", name: "api.profile_listing.by_place", methods: "GET", format: "json")]
  2362.     #[Route("/api/profiles/listing/city/{city}/place/{placeType}/{takeOutLocation}", name: "api.profile_listing.by_place.take_out_location", methods: "GET", format: "json")]
  2363.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2364.     #[OA\Tag(name: "ProfileListing")]
  2365.     #[OA\Response(
  2366.         response: 200,
  2367.         description: '',
  2368.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2369.     )]
  2370.     public function listByPlace(Request $request, City $city, string $placeType, string $takeOutLocation = null): Response
  2371.     {
  2372.         if ('take-out' === $placeType && null !== $takeOutLocation && null === TakeOutLocations::getValueByUriIdentity($takeOutLocation)) {
  2373.             throw $this->createNotFoundException();
  2374.         }
  2375.         $specs = $this->profileListSpecificationService->listByPlace($placeType, $takeOutLocation);
  2376.         if (null === $specs) {
  2377.             throw $this->createNotFoundException();
  2378.         }
  2379.         $response = new Response();
  2380.         $alternativeSpec = $this->getORSpecForItemsArray(TakeOutLocations::getList(), function($item): ProfileIsProvidingTakeOut {
  2381.             return new ProfileIsProvidingTakeOut($item);
  2382.         });
  2383.         if ($placeType === 'take-out') {
  2384.             $alternativeSpec->orX(new ProfileHasApartments());
  2385.         }
  2386.         $apiEndpoint = '/city/{city}/place/'.$placeType;
  2387.         if (null !== $takeOutLocation) {
  2388.             $apiEndpoint .= '/'.$takeOutLocation;
  2389.         }
  2390.         $result = $this->paginatedListing($city, $apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2391.         if ($this->isApiRequest($request)) {
  2392.             return $this->jsonListingResponse($result, $city, $specs);
  2393.         }
  2394.         return $this->render('ProfileList/list.html.twig', [
  2395.             'profiles' => $result,
  2396.             'source' => $this->source,
  2397.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2398.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2399.                 'city' => $city->getUriIdentity(),
  2400.                 'placeType' => $placeType,
  2401.                 'takeOutLocation' => TakeOutLocations::getUriIdentity(TakeOutLocations::getValueByUriIdentity($takeOutLocation)),
  2402.                 'page' => $this->getCurrentPageNumber()
  2403.             ]),
  2404.             'recommendationSpec' => $specs->recommendationSpec(),
  2405.         ], response: $response);
  2406.     }
  2407.     #[ParamConverter("city", converter: "city_converter")]
  2408.     #[Route("/api/profiles/listing/city/{city}/privatehaircut/{privateHaircut}", name: "api.profile_listing.by_privatehaircut", methods: "GET", format: "json")]
  2409.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2410.     #[OA\Tag(name: "ProfileListing")]
  2411.     #[OA\Response(
  2412.         response: 200,
  2413.         description: '',
  2414.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2415.     )]
  2416.     public function listByPrivateHaircut(Request $request, City $city, string $privateHaircut): Response
  2417.     {
  2418.         if(null === $type = PrivateHaircuts::getValueByUriIdentity($privateHaircut))
  2419.             throw $this->createNotFoundException();
  2420.         $specs = $this->profileListSpecificationService->listByPrivateHaircut($privateHaircut);
  2421.         $response = new Response();
  2422.         $apiEndpoint = '/city/{city}/privatehaircut/'.$type;
  2423.         $alternativeSpec = $this->getORSpecForItemsArray(PrivateHaircuts::getList(), function($item): ProfileWithPrivateHaircut {
  2424.             return new ProfileWithPrivateHaircut($item);
  2425.         });
  2426.         $result = $this->paginatedListing($city, $apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2427.         if ($this->isApiRequest($request)) {
  2428.             return $this->jsonListingResponse($result, $city, $specs);
  2429.         }
  2430.         return $this->render('ProfileList/list.html.twig', [
  2431.             'profiles' => $result,
  2432.             'source' => $this->source,
  2433.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2434.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2435.                 'city' => $city->getUriIdentity(),
  2436.                 'privateHaircut' => $privateHaircut,
  2437.                 'page' => $this->getCurrentPageNumber()
  2438.             ]),
  2439.             'recommendationSpec' => $specs->recommendationSpec(),
  2440.         ], response: $response);
  2441.     }
  2442.     #[ParamConverter("city", converter: "city_converter")]
  2443.     #[Route("/api/profiles/listing/city/{city}/nationality/{nationality}", name: "api.profile_listing.by_nationality", methods: "GET", format: "json")]
  2444.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2445.     #[OA\Tag(name: "ProfileListing")]
  2446.     #[OA\Response(
  2447.         response: 200,
  2448.         description: '',
  2449.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2450.     )]
  2451.     public function listByNationality(Request $request, City $city, string $nationality): Response
  2452.     {
  2453.         if (null === $type = Nationalities::getValueByUriIdentity($nationality))
  2454.             throw $this->createNotFoundException();
  2455.         $specs = $this->profileListSpecificationService->listByNationality($nationality);
  2456.         $response = new Response();
  2457.         $alternativeSpec = $this->getORSpecForItemsArray(Nationalities::getList(), function($item): ProfileWithNationality {
  2458.             return new ProfileWithNationality($item);
  2459.         });
  2460.         $apiEndpoint = '/city/{city}/nationality/'.$type;
  2461.         $result = $this->paginatedListing($city, $apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2462.         if ($this->isApiRequest($request)) {
  2463.             return $this->jsonListingResponse($result, $city, $specs);
  2464.         }
  2465.         return $this->render('ProfileList/list.html.twig', [
  2466.             'profiles' => $result,
  2467.             'source' => $this->source,
  2468.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2469.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2470.                 'city' => $city->getUriIdentity(),
  2471.                 'nationality' => $nationality,
  2472.                 'page' => $this->getCurrentPageNumber()
  2473.             ]),
  2474.             'recommendationSpec' => $specs->recommendationSpec(),
  2475.         ], response: $response);
  2476.     }
  2477.     #[ParamConverter("city", converter: "city_converter")]
  2478.     #[Route("/api/profiles/listing/city/{city}/appearance/{appearance}", name: "api.profile_listing.by_appearance", methods: "GET", format: "json")]
  2479.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2480.     #[OA\Tag(name: "ProfileListing")]
  2481.     #[OA\Response(
  2482.         response: 200,
  2483.         description: '',
  2484.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2485.     )]
  2486.     public function listByAppearance(Request $request, City $city, string $appearance): Response
  2487.     {
  2488.         if (null === $type = Appearances::getValueByUriIdentity($appearance))
  2489.             throw $this->createNotFoundException();
  2490.         $specs = $this->profileListSpecificationService->listByAppearance($appearance);
  2491.         $response = new Response();
  2492.         $alternativeSpec = $this->getORSpecForItemsArray(Appearances::getList(), function($item): ProfileWithAppearance {
  2493.             return new ProfileWithAppearance($item);
  2494.         });
  2495.         $apiEndpoint = '/city/{city}/appearance/'.$type;
  2496.         $result = $this->paginatedListing($city, $apiEndpoint, ['city' => $city->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_BY_PARAMS, $response);
  2497.         if ($this->isApiRequest($request)) {
  2498.             return $this->json($result);
  2499.         }
  2500.         return $this->render('ProfileList/list.html.twig', [
  2501.             'profiles' => $result,
  2502.             'source' => $this->source,
  2503.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2504.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2505.                 'city' => $city->getUriIdentity(),
  2506.                 'appearance' => $appearance,
  2507.                 'page' => $this->getCurrentPageNumber()
  2508.             ]),
  2509.             'recommendationSpec' => $specs->recommendationSpec(),
  2510.         ], response: $response);
  2511.     }
  2512.     #[ParamConverter("city", converter: "city_converter")]
  2513.     #[ParamConverter("service", options: ['mapping' => ['service' => 'uriIdentity']])]
  2514.     #[Route("/api/profiles/listing/city/{city}/service/{service}", name: "api.profile_listing.by_service", methods: "GET", format: "json")]
  2515.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2516.     #[OA\Tag(name: "ProfileListing")]
  2517.     #[OA\Response(
  2518.         response: 200,
  2519.         description: '',
  2520.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2521.     )]
  2522.     public function listByProvidedService(Request $request, City $city, Service $service): Response
  2523.     {
  2524.         $specs = $this->profileListSpecificationService->listByProvidedService($service, $city);
  2525.         $response = new Response();
  2526.         $sameGroupServices = $this->serviceRepository->findBy(['group' => $service->getGroup()]);
  2527.         $alternativeSpec = $this->getORSpecForItemsArray([$sameGroupServices], function($item): ProfileIsProvidingOneOfServices {
  2528.             return new ProfileIsProvidingOneOfServices($item);
  2529.         });
  2530.         $result = $this->paginatedListing($city, '/city/{city}/service/{service}', ['city' => $city->getId(), 'service' => $service->getId()], $specs->spec(), $alternativeSpec, self::RESULT_SOURCE_SERVICE, $response);
  2531.         if ($this->isApiRequest($request)) {
  2532.             return $this->jsonListingResponse($result, $city, $specs);
  2533.         }
  2534.         return $this->render('ProfileList/list.html.twig', [
  2535.             'profiles' => $result,
  2536.             'source' => $this->source,
  2537.             'source_default' => self::RESULT_SOURCE_SERVICE,
  2538.             'service' => $service,
  2539.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2540.                 'city' => $city->getUriIdentity(),
  2541.                 'service' => $service->getUriIdentity(),
  2542.                 'page' => $this->getCurrentPageNumber()
  2543.             ]),
  2544.             'recommendationSpec' => $specs->recommendationSpec(),
  2545.         ], response: $response);
  2546.     }
  2547.     #[ParamConverter("city", converter: "city_converter")]
  2548.     #[ParamConverter("item", options: ['mapping' => ['item' => 'uriIdentity']])]
  2549.     #[Route("/api/profiles/listing/city/{city}/inventory-item/{item}", name: "api.profile_listing.by_inventory_item", methods: "GET", format: "json")]
  2550.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2551.     #[OA\Tag(name: "ProfileListing")]
  2552.     #[OA\Response(
  2553.         response: 200,
  2554.         description: '',
  2555.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2556.     )]
  2557.     public function listByInventoryItem(Request $request, City $city, ProfileInventoryItem $item): Response
  2558.     {
  2559.         if (!$this->isInventoryItemTypeEnabled($item)) {
  2560.             throw $this->createNotFoundException();
  2561.         }
  2562.         $specs = $this->profileListSpecificationService->listByInventoryItem($item);
  2563.         $response = new Response();
  2564.         $result = $this->paginatedListing($city, '/city/{city}/inventory-item/{item}', ['city' => $city->getId(), 'item' => $item->getId()], $specs->spec(), null, self::RESULT_SOURCE_PROFILE_INVENTORY_ITEM, $response);
  2565.         $request->attributes->set('profiles_count', $result->totalCount());
  2566.         if ($this->isApiRequest($request)) {
  2567.             return $this->json($result);
  2568.         }
  2569.         return $this->render('ProfileList/list.html.twig', [
  2570.             'profiles' => $result,
  2571.             'source' => $this->source,
  2572.             'source_default' => self::RESULT_SOURCE_PROFILE_INVENTORY_ITEM,
  2573.             'inventory_item' => $item,
  2574.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2575.                 'city' => $city->getUriIdentity(),
  2576.                 'item' => $item->getUriIdentity(),
  2577.                 'page' => $this->getCurrentPageNumber()
  2578.             ]),
  2579.             'recommendationSpec' => $specs->recommendationSpec(),
  2580.         ], response: $response);
  2581.     }
  2582.     private function isInventoryItemTypeEnabled(ProfileInventoryItem $item): bool
  2583.     {
  2584.         return match ($item->getType()) {
  2585.             ProfileInventoryItemType::SEX_TOY => $this->features->sex_toys(),
  2586.             ProfileInventoryItemType::CLOTHES_AND_UNDERWEAR => $this->features->clothes_and_underwear(),
  2587.             ProfileInventoryItemType::ROLE_PLAYING_COSTUME => $this->features->role_playing_costumes(),
  2588.             default => false,
  2589.         };
  2590.     }
  2591.     /**
  2592.      * @Feature("list_names")
  2593.      */
  2594.     #[ParamConverter("city", converter: "city_converter")]
  2595.     #[Route("/api/profiles/listing/city/{city}/names", name: "api.profile_listing.names", methods: "GET", format: "json")]
  2596.     #[OA\Tag(name: "ProfileListing")]
  2597.     #[OA\Response(
  2598.         response: 200,
  2599.         description: 'Список имен анкет по городу',
  2600.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileNamesPage")
  2601.     )]
  2602.     #[Cache(maxage: 3600, public: true)]
  2603.     public function listNames(Request $request, City $city): Response
  2604.     {
  2605.         $names = $this->profileNameLandingProvider->groupedNamesByCity($city);
  2606.         $popularNames = $this->profileNameLandingProvider->popularNamesByCity($city);
  2607.         if ($this->isApiRequest($request)) {
  2608.             return $this->json([
  2609.                 'data' => $names,
  2610.                 'popularNames' => $popularNames,
  2611.             ]);
  2612.         }
  2613.         return parent::render('ProfileNameList/list.html.twig', [
  2614.             'names' => $names,
  2615.             'popularNames' => $popularNames,
  2616.         ]);
  2617.     }
  2618.     /**
  2619.      * @Feature("list_names")
  2620.      */
  2621.     #[ParamConverter("city", converter: "city_converter")]
  2622.     #[Route("/api/profiles/listing/city/{city}/name/{name}", name: "api.profile_listing.by_name", methods: "GET", format: "json")]
  2623.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2624.     #[OA\Tag(name: "ProfileListing")]
  2625.     #[OA\Response(
  2626.         response: 200,
  2627.         description: 'Листинг анкет по имени',
  2628.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileNameListingPage")
  2629.     )]
  2630.     #[Cache(maxage: 3600, public: true)]
  2631.     public function listByName(Request $request, City $city, string $name): Response
  2632.     {
  2633.         $profileName = $this->profileNameLandingProvider->findNameByUri($city, $name);
  2634.         if (null === $profileName) {
  2635.             throw $this->createNotFoundException();
  2636.         }
  2637.         $request->attributes->set('profileName', $profileName);
  2638.         $specs = new ProfileListSpecification(new ProfileIdIn($profileName['profileIds']), null);
  2639.         $response = new Response();
  2640.         $result = $this->paginatedListing(
  2641.             $city,
  2642.             '/city/{city}/name/{name}',
  2643.             ['city' => $city->getId(), 'name' => $profileName['uriIdentity']],
  2644.             $specs->spec(),
  2645.             null,
  2646.             null,
  2647.             $response
  2648.         );
  2649.         $response->setMaxAge(3600);
  2650.         if ($this->isApiRequest($request)) {
  2651.             return $this->jsonListingResponse($result, $city, $specs, [
  2652.                 'popularNames' => $this->profileNameLandingProvider->popularNamesByCity($city),
  2653.             ]);
  2654.         }
  2655.         return $this->render('ProfileNameList/by_name.html.twig', [
  2656.             'profiles' => $result,
  2657.             'profileName' => $profileName,
  2658.             'popularNames' => $this->profileNameLandingProvider->popularNamesByCity($city),
  2659.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2660.                 'city' => $city->getUriIdentity(),
  2661.                 'name' => $profileName['uriIdentity'],
  2662.                 'page' => $this->getCurrentPageNumber(),
  2663.             ]),
  2664.             'recommendationSpec' => $specs->recommendationSpec(),
  2665.         ], response: $response);
  2666.     }
  2667.     /**
  2668.      * @Feature("has_archive_page")
  2669.      */
  2670.     #[ParamConverter("city", converter: "city_converter")]
  2671.     #[Route("/api/profiles/listing/city/{city}/archive", name: "api.profile_listing.archive", methods: "GET", format: "json")]
  2672.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2673.     #[OA\Tag(name: "ProfileListing")]
  2674.     #[OA\Response(
  2675.         response: 200,
  2676.         description: '',
  2677.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2678.     )]
  2679.     public function listArchived(Request $request, City $city): Response
  2680.     {
  2681.         $result = $this->profileList->list($city, null, null, null, false, null, ProfileList::ORDER_BY_UPDATED);
  2682.         if ($this->isApiRequest($request)) {
  2683.             return $this->jsonListingResponse($result, $city, null);
  2684.         }
  2685.         return $this->render('ProfileList/list.html.twig', [
  2686.             'profiles' => $result,
  2687.             'recommendationSpec' => new \App\Specification\ElasticSearch\ProfileIsNotArchived(), //ProfileIsArchived, согласно https://redminez.net/issues/28305 в реках выводятся неарзивные
  2688.         ]);
  2689.     }
  2690.     #[ParamConverter("city", converter: "city_converter")]
  2691.     #[Route("/api/profiles/listing/city/{city}/recent", name: "api.profile_listing.recent", methods: "GET", format: "json")]
  2692.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2693.     #[OA\Tag(name: "ProfileListing")]
  2694.     #[OA\Response(
  2695.         response: 200,
  2696.         description: '',
  2697.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2698.     )]
  2699.     public function listNew(Request $request, City $city, int $weeks = 2): Response
  2700.     {
  2701.         $specs = $this->profileListSpecificationService->listNew($weeks);
  2702.         $response = new Response();
  2703.         $result = $this->profileList->listLatestCreated($city, self::NEW_PROFILES_LIMIT);
  2704.         if ($this->isApiRequest($request)) {
  2705.             return $this->jsonListingResponse($result, $city, $specs);
  2706.         }
  2707.         return $this->render('ProfileList/list.html.twig', [
  2708.             'profiles' => $result,
  2709.             'recommendationSpec' => $specs->recommendationSpec(),
  2710.         ], response: $response);
  2711.     }
  2712.     #[ParamConverter("city", converter: "city_converter")]
  2713.     #[Route("/api/profiles/listing/city/{city}/noretouch", name: "api.profile_listing.noretouch", methods: "GET", format: "json")]
  2714.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2715.     #[OA\Tag(name: "ProfileListing")]
  2716.     #[OA\Response(
  2717.         response: 200,
  2718.         description: '',
  2719.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2720.     )]
  2721.     public function listByNoRetouch(Request $request, City $city): Response
  2722.     {
  2723.         $specs = $this->profileListSpecificationService->listByNoRetouch();
  2724.         $response = new Response();
  2725.         $result = $this->paginatedListing($city, '/city/{city}/noretouch', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2726.         if ($this->isApiRequest($request)) {
  2727.             return $this->jsonListingResponse($result, $city, $specs);
  2728.         }
  2729.         return $this->render('ProfileList/list.html.twig', [
  2730.             'profiles' => $result,
  2731.             'profiles_count' => $result->count(),
  2732.             'source' => $this->source,
  2733.             'recommendationSpec' => $specs->recommendationSpec(),
  2734.         ], response: $response);
  2735.     }
  2736.     #[ParamConverter("city", converter: "city_converter")]
  2737.     #[Route("/api/profiles/listing/city/{city}/nice", name: "api.profile_listing.nice", methods: "GET", format: "json")]
  2738.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2739.     #[OA\Tag(name: "ProfileListing")]
  2740.     #[OA\Response(
  2741.         response: 200,
  2742.         description: '',
  2743.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2744.     )]
  2745.     public function listByNice(Request $request, City $city): Response
  2746.     {
  2747.         $specs = $this->profileListSpecificationService->listByNice();
  2748.         $response = new Response();
  2749.         $result = $this->paginatedListing($city, '/city/{city}/nice', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2750.         if ($this->isApiRequest($request)) {
  2751.             return $this->jsonListingResponse($result, $city, $specs);
  2752.         }
  2753.         return $this->render('ProfileList/list.html.twig', [
  2754.             'profiles' => $result,
  2755.             'source' => $this->source,
  2756.             'recommendationSpec' => $specs->recommendationSpec(),
  2757.         ], response: $response);
  2758.     }
  2759.     #[ParamConverter("city", converter: "city_converter")]
  2760.     #[Route("/api/profiles/listing/city/{city}/oncall", name: "api.profile_listing.oncall", methods: "GET", format: "json")]
  2761.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2762.     #[OA\Tag(name: "ProfileListing")]
  2763.     #[OA\Response(
  2764.         response: 200,
  2765.         description: '',
  2766.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2767.     )]
  2768.     public function listByOnCall(Request $request, City $city): Response
  2769.     {
  2770.         $specs = $this->profileListSpecificationService->listByOnCall();
  2771.         $response = new Response();
  2772.         $result = $this->paginatedListing($city, '/city/{city}/oncall', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2773.         if ($this->isApiRequest($request)) {
  2774.             return $this->jsonListingResponse($result, $city, $specs);
  2775.         }
  2776.         return $this->render('ProfileList/list.html.twig', [
  2777.             'profiles' => $result,
  2778.             'source' => $this->source,
  2779.             'recommendationSpec' => $specs->recommendationSpec(),
  2780.         ], response: $response);
  2781.     }
  2782.     #[ParamConverter("city", converter: "city_converter")]
  2783.     #[Route("/api/profiles/listing/city/{city}/fornight", name: "api.profile_listing.fornight", methods: "GET", format: "json")]
  2784.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2785.     #[OA\Tag(name: "ProfileListing")]
  2786.     #[OA\Response(
  2787.         response: 200,
  2788.         description: '',
  2789.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2790.     )]
  2791.     public function listForNight(Request $request, City $city): Response
  2792.     {
  2793.         $specs = $this->profileListSpecificationService->listForNight();
  2794.         $response = new Response();
  2795.         $result = $this->paginatedListing($city, '/city/{city}/fornight', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2796.         if ($this->isApiRequest($request)) {
  2797.             return $this->jsonListingResponse($result, $city, $specs);
  2798.         }
  2799.         return $this->render('ProfileList/list.html.twig', [
  2800.             'profiles' => $result,
  2801.             'source' => $this->source,
  2802.             'recommendationSpec' => $specs->recommendationSpec(),
  2803.         ], response: $response);
  2804.     }
  2805.     private function getSpecForEliteGirls(City $city): Filter
  2806.     {
  2807.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  2808.             'RUB' => 5000,
  2809.             'UAH' => 1500,
  2810.             'USD' => 100,
  2811.             'EUR' => 130,
  2812.         ]);
  2813.         return new ProfileIsElite($minPrice);
  2814.     }
  2815.     private function getElasticSearchSpecForEliteGirls(City $city): ISpecification
  2816.     {
  2817.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  2818.             'RUB' => 5000,
  2819.             'UAH' => 1500,
  2820.             'USD' => 100,
  2821.             'EUR' => 130,
  2822.         ]);
  2823.         return new \App\Specification\ElasticSearch\ProfileIsElite($minPrice);
  2824.     }
  2825.     #[ParamConverter("city", converter: "city_converter")]
  2826.     #[Route("/api/profiles/listing/city/{city}/elite", name: "api.profile_listing.elite", methods: "GET", format: "json")]
  2827.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2828.     #[OA\Tag(name: "ProfileListing")]
  2829.     #[OA\Response(
  2830.         response: 200,
  2831.         description: '',
  2832.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2833.     )]
  2834.     public function listForEliteGirls(CountryCurrencyResolver $countryCurrencyResolver, Request $request, City $city): Response
  2835.     {
  2836.         $specs = $this->profileListSpecificationService->listForEliteGirls($city);
  2837.         $response = new Response();
  2838.         $result = $this->paginatedListing($city, '/city/{city}/elite', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  2839.         if ($this->isApiRequest($request)) {
  2840.             return $this->jsonListingResponse($result, $city, $specs);
  2841.         }
  2842.         return $this->render('ProfileList/list.html.twig', [
  2843.             'profiles' => $result,
  2844.             'source' => $this->source,
  2845.             'source_default' => self::RESULT_SOURCE_BY_PARAMS,
  2846.             'category_url' => $this->generateUrl($request->attributes->get('_route'), [
  2847.                 'city' => $city->getUriIdentity(),
  2848.                 'page' => $this->getCurrentPageNumber()
  2849.             ]),
  2850.             'recommendationSpec' => $specs->recommendationSpec(),
  2851.         ], response: $response);
  2852.     }
  2853.     #[ParamConverter("city", converter: "city_converter")]
  2854.     #[Route("/api/profiles/listing/city/{city}/realelite", name: "api.profile_listing.real_elite", methods: "GET", format: "json")]
  2855.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2856.     #[OA\Tag(name: "ProfileListing")]
  2857.     #[OA\Response(
  2858.         response: 200,
  2859.         description: '',
  2860.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2861.     )]
  2862.     public function listForRealElite(Request $request, CountryCurrencyResolver $countryCurrencyResolver, City $city): Response
  2863.     {
  2864.         $specs = $this->profileListSpecificationService->listForRealElite($city);
  2865.         $response = new Response();
  2866.         $result = $this->paginatedListing($city, '/city/{city}/realelite', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2867.         if ($this->isApiRequest($request)) {
  2868.             return $this->jsonListingResponse($result, $city, $specs);
  2869.         }
  2870.         return $this->render('ProfileList/list.html.twig', [
  2871.             'profiles' => $result,
  2872.             'source' => $this->source,
  2873.             'recommendationSpec' => $specs->recommendationSpec(),
  2874.         ], response: $response);
  2875.     }
  2876.     #[ParamConverter("city", converter: "city_converter")]
  2877.     #[Route("/api/profiles/listing/city/{city}/vip", name: "api.profile_listing.vip", methods: "GET", format: "json")]
  2878.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2879.     #[OA\Tag(name: "ProfileListing")]
  2880.     #[OA\Response(
  2881.         response: 200,
  2882.         description: '',
  2883.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2884.     )]
  2885.     public function listForVipPros(Request $request, CountryCurrencyResolver $countryCurrencyResolver, City $city): Response
  2886.     {
  2887.         $specs = $this->profileListSpecificationService->listForVipPros($city);
  2888.         $response = new Response();
  2889.         $result = $this->paginatedListing($city, '/city/{city}/vip', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2890.         if ($this->isApiRequest($request)) {
  2891.             return $this->jsonListingResponse($result, $city, $specs);
  2892.         }
  2893.         return $this->render('ProfileList/list.html.twig', [
  2894.             'profiles' => $result,
  2895.             'source' => $this->source,
  2896.             'recommendationSpec' => $specs->recommendationSpec(),
  2897.         ], response: $response);
  2898.     }
  2899.     #[ParamConverter("city", converter: "city_converter")]
  2900.     #[Route("/api/profiles/listing/city/{city}/vipindi", name: "api.profile_listing.vipindi", methods: "GET", format: "json")]
  2901.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2902.     #[OA\Tag(name: "ProfileListing")]
  2903.     #[OA\Response(
  2904.         response: 200,
  2905.         description: '',
  2906.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2907.     )]
  2908.     public function listForVipIndividual(Request $request, CountryCurrencyResolver $countryCurrencyResolver, City $city): Response
  2909.     {
  2910.         $specs = $this->profileListSpecificationService->listForVipIndividual($city);
  2911.         $response = new Response();
  2912.         $result = $this->paginatedListing($city, '/city/{city}/vipindi', ['city' => $city->getId()], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2913.         if ($this->isApiRequest($request)) {
  2914.             return $this->jsonListingResponse($result, $city, $specs);
  2915.         }
  2916.         return $this->render('ProfileList/list.html.twig', [
  2917.             'profiles' => $result,
  2918.             'source' => $this->source,
  2919.             'recommendationSpec' => $specs->recommendationSpec(),
  2920.         ], response: $response);
  2921.     }
  2922.     #[ParamConverter("city", converter: "city_converter")]
  2923.     #[Route("/api/profiles/listing/city/{city}/vipgirls", name: "api.profile_listing.vipgirls", methods: "GET", format: "json")]
  2924.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2925.     #[OA\Tag(name: "ProfileListing")]
  2926.     #[OA\Response(
  2927.         response: 200,
  2928.         description: '',
  2929.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2930.     )]
  2931.     public function listForVipGirlsCity(Request $request, City $city): Response
  2932.     {
  2933.         $specs = $this->profileListSpecificationService->listForVipGirlsCity($city);
  2934.         $response = new Response();
  2935.         $result = $this->paginatedListing($city, null, [], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2936.         if ($this->isApiRequest($request)) {
  2937.             return $this->jsonListingResponse($result, $city, $specs);
  2938.         }
  2939.         return $this->render('ProfileList/list.html.twig', [
  2940.             'profiles' => $result,
  2941.             'source' => $this->source,
  2942.             'recommendationSpec' => $specs->recommendationSpec(),
  2943.         ], response: $response);
  2944.     }
  2945.     #[ParamConverter("city", converter: "city_converter")]
  2946.     #[Route("/api/profiles/listing/city/{city}/girlfriends", name: "api.profile_listing.girlfriends", methods: "GET", format: "json")]
  2947.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2948.     #[OA\Tag(name: "ProfileListing")]
  2949.     #[OA\Response(
  2950.         response: 200,
  2951.         description: '',
  2952.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2953.     )]
  2954.     public function listOfGirlfriends(Request $request, City $city): Response
  2955.     {
  2956.         $specs = $this->profileListSpecificationService->listOfGirlfriends();
  2957.         $response = new Response();
  2958.         $result = $this->paginatedListing($city, null, [], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2959.         if ($this->isApiRequest($request)) {
  2960.             return $this->jsonListingResponse($result, $city, $specs);
  2961.         }
  2962.         return $this->render('ProfileList/list.html.twig', [
  2963.             'profiles' => $result,
  2964.             'source' => $this->source,
  2965.             'recommendationSpec' => $specs->recommendationSpec(),
  2966.         ]);
  2967.     }
  2968.     #[ParamConverter("city", converter: "city_converter")]
  2969.     #[Route("/api/profiles/listing/city/{city}/most_expensive", name: "api.profile_listing.most_expensive", methods: "GET", format: "json")]
  2970.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2971.     #[OA\Tag(name: "ProfileListing")]
  2972.     #[OA\Response(
  2973.         response: 200,
  2974.         description: '',
  2975.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2976.     )]
  2977.     public function listOfMostExpensive(Request $request, City $city): Response
  2978.     {
  2979.         $specs = $this->profileListSpecificationService->listOfMostExpensive($city);
  2980.         $response = new Response();
  2981.         $result = $this->paginatedListing($city, null, [], $specs->spec(), null, self::RESULT_SOURCE_CITY, $response);
  2982.         if ($this->isApiRequest($request)) {
  2983.             return $this->jsonListingResponse($result, $city, $specs);
  2984.         }
  2985.         return $this->render('ProfileList/list.html.twig', [
  2986.             'profiles' => $result,
  2987.             'source' => $this->source,
  2988.             'recommendationSpec' => $specs->recommendationSpec(),
  2989.         ]);
  2990.     }
  2991.     #[ParamConverter("city", converter: "city_converter")]
  2992.     #[Route("/api/profiles/listing/city/{city}/bdsm", name: "api.profile_listing.bdsm", methods: "GET", format: "json")]
  2993.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  2994.     #[OA\Tag(name: "ProfileListing")]
  2995.     #[OA\Response(
  2996.         response: 200,
  2997.         description: '',
  2998.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  2999.     )]
  3000.     public function listBdsm(Request $request, City $city, ServiceRepository $serviceRepository, ParameterBagInterface $parameterBag): Response
  3001.     {
  3002.         $specs = $this->profileListSpecificationService->listBdsm();
  3003.         $response = new Response();
  3004.         $result = $this->paginatedListing($city, '/city/{city}/bdsm', ['city' => $city->getId()], $specs->spec(), null, null, $response);
  3005.         if ($this->isApiRequest($request)) {
  3006.             return $this->jsonListingResponse($result, $city, $specs);
  3007.         }
  3008.         return $this->render('ProfileList/list.html.twig', [
  3009.             'profiles' => $result,
  3010.             'recommendationSpec' => $specs->recommendationSpec(),
  3011.         ], response: $response);
  3012.     }
  3013.     #[ParamConverter("city", converter: "city_converter")]
  3014.     #[Route("/api/profiles/listing/city/{city}/gender/{gender}", name: "api.profile_listing.by_gender", methods: "GET", format: "json")]
  3015.     #[OA\Parameter(name: "page", in: "query", schema: new OA\Schema(type: "integer", default: 1, minimum: 1))]
  3016.     #[OA\Tag(name: "ProfileListing")]
  3017.     #[OA\Response(
  3018.         response: 200,
  3019.         description: '',
  3020.         content: new OA\JsonContent(ref: "#/components/schemas/ProfileListingPage")
  3021.     )]
  3022.     public function listByGender(Request $request, City $city, string $gender, DefaultCityProvider $defaultCityProvider): Response
  3023.     {
  3024.         if ($city->getId() != $defaultCityProvider->getDefaultCity()->getId()) {
  3025.             throw $this->createNotFoundException();
  3026.         }
  3027.         if (null === Genders::getValueByUriIdentity($gender))
  3028.             throw $this->createNotFoundException();
  3029.         $specs = $this->profileListSpecificationService->listByGender($gender);
  3030.         $response = new Response();
  3031.         $result = $this->paginatedListing($city, null, [], $specs->spec(), null, null, $response);
  3032.         if ($this->isApiRequest($request)) {
  3033.             return $this->jsonListingResponse($result, $city, $specs);
  3034.         }
  3035.         return $this->render('ProfileList/list.html.twig', [
  3036.             'profiles' => $result,
  3037.             'recommendationSpec' => $specs->recommendationSpec(),
  3038.         ]);
  3039.     }
  3040.     /**
  3041.      * Сейчас не используется, решили доставать их всех соседних подкатегорий разом.
  3042.      * Пока оставил, вдруг передумают.
  3043.      * @deprecated
  3044.      */
  3045.     public function listByNextSimilarCategories(callable $listMethod, $requestCategory, array $similarItems): ORMQueryResult
  3046.     {
  3047.         $similarItems = array_filter($similarItems, function ($item) use ($requestCategory): bool {
  3048.             return $item != $requestCategory;
  3049.         });
  3050.         //shuffle($similarItems);
  3051.         $item = null;
  3052.         $result = null;
  3053.         do {
  3054.             $item = $item == null ? current($similarItems) : next($similarItems);
  3055.             if (false === $item)
  3056.                 return $result;
  3057.             $result = $listMethod($item);
  3058.         } while ($result->count() == 0);
  3059.         return $result;
  3060.     }
  3061.     private function shouldShowHomepageCityListingsBlock(City $city, int $page, bool $subRequest): bool
  3062.     {
  3063.         if ($page !== 1) {
  3064.             return false;
  3065.         }
  3066.         if ($subRequest) {
  3067.             return true;
  3068.         }
  3069.         return !$city->equals($this->parameterBag->get('default_city'));
  3070.     }
  3071.     protected function getCurrentPageNumber(): int
  3072.     {
  3073.         $page = (int) $this->requestStack->getCurrentRequest()?->get($this->pageParameter, 1);
  3074.         if ($page < 1) {
  3075.             $page = 1;
  3076.         }
  3077.         return $page;
  3078.     }
  3079.     protected function render(string $view, array $parameters = [], Response $response = null): Response
  3080.     {
  3081.         $skipListingRecommendations = !empty($parameters['skip_listing_recommendations']);
  3082.         if (!$skipListingRecommendations) {
  3083.             $parameters = $this->fillListingWithRecommendations($parameters);
  3084.         }
  3085.         $this->listingService->setCurrentListingPage($parameters['profiles']);
  3086.         $requestAttrs = $this->requestStack->getCurrentRequest();
  3087.         if (isset($parameters['profiles']) && is_object($parameters['profiles']) && method_exists($parameters['profiles'], 'totalCount')) {
  3088.             $requestAttrs->attributes->set('profiles_count', $parameters['profiles']->totalCount());
  3089.         }
  3090.         $listing = $requestAttrs->get('_controller');
  3091.         $listing = is_array($listing) ? $listing[count($listing) - 1] : $listing;
  3092.         $listing = preg_replace('/[^:]+::/', '', $listing);
  3093.         $listingParameters = $requestAttrs->get('_route_params');
  3094.         $listingParameters = is_array($listingParameters) ? $listingParameters : [];
  3095.         $mainRequestHasPageParam = isset(($this->requestStack->getMainRequest()->get('_route_params') ?? [])['page']);
  3096.         if ($this->requestStack->getCurrentRequest()->isXmlHttpRequest()) {
  3097.             $view = (
  3098.                 str_starts_with($listing, 'list')
  3099.                 && 'ProfileList/list.html.twig' === $view
  3100.                 && $mainRequestHasPageParam //isset($listingParameters['page'])
  3101.             )
  3102.                 ? 'ProfileList/list.profiles.html.twig'
  3103.                 : $view;
  3104.             return $this->prepareForXhr(parent::render($view, $parameters, $response));
  3105.             //return $this->getJSONResponse($parameters);
  3106.         } else {
  3107.             if (!$skipListingRecommendations) {
  3108.                 $parameters = $this->prepareSsrRecommendedProfiles($parameters);
  3109.             }
  3110.             $parameters = array_merge($parameters, [
  3111.                 'listing' => $listing,
  3112.                 'listing_parameters' => $listingParameters,
  3113.             ]);
  3114.             return parent::render($view, $parameters, $response);
  3115.         }
  3116.     }
  3117.     private function fillListingWithRecommendations(array $parameters): array
  3118.     {
  3119.         $parameters['recommendations_fill_applied'] = $parameters['recommendations_fill_applied'] ?? false;
  3120.         if (!($parameters['profiles'] ?? null) instanceof Page) {
  3121.             return $parameters;
  3122.         }
  3123.         $city = $this->requestStack->getCurrentRequest()?->attributes->get('city');
  3124.         if (!$city instanceof City) {
  3125.             return $parameters;
  3126.         }
  3127.         $filled = $this->profileListingRecommendationsFiller->fill(
  3128.             $city,
  3129.             $parameters['profiles'],
  3130.             $this->resolveRecommendationGenders(),
  3131.         );
  3132.         $parameters['profiles'] = $filled['profiles'];
  3133.         $parameters['recommendations_fill_applied'] = $filled['applied'];
  3134.         $parameters['recommendations_fill_original_count'] = $filled['original_count'];
  3135.         $parameters['excludeRecommendationProfileIds'] = array_values(array_unique(array_merge(
  3136.             $parameters['excludeRecommendationProfileIds'] ?? [],
  3137.             $filled['visible_profile_ids'] ?? [],
  3138.         )));
  3139.         return $parameters;
  3140.     }
  3141.     private function prepareSsrRecommendedProfiles(array $parameters): array
  3142.     {
  3143.         if (!empty($parameters['recommendations_fill_applied'])) {
  3144.             return $parameters;
  3145.         }
  3146.         if (isset($parameters['ssr_recommended_profiles']) || !($parameters['profiles'] ?? null) instanceof Page) {
  3147.             return $parameters;
  3148.         }
  3149.         $city = $this->requestStack->getCurrentRequest()?->attributes->get('city');
  3150.         if (!$city instanceof City) {
  3151.             return $parameters;
  3152.         }
  3153.         $parameters['ssr_recommended_profiles'] = $this->profileListingRecommendationsFiller->popularProfilesForBlock(
  3154.             $city,
  3155.             $parameters['profiles'],
  3156.             $this->resolveRecommendationGenders(),
  3157.             5,
  3158.             $parameters['excludeRecommendationProfileIds'] ?? [],
  3159.         );
  3160.         return $parameters;
  3161.     }
  3162.     private function resolveRecommendationGenders(): array
  3163.     {
  3164.         $gender = $this->requestStack->getCurrentRequest()?->attributes->get('gender');
  3165.         if (is_string($gender) && null !== Genders::getValueByUriIdentity($gender)) {
  3166.             return [Genders::getValueByUriIdentity($gender)];
  3167.         }
  3168.         return [Genders::FEMALE];
  3169.     }
  3170.     private function listActiveWithinCityOrderedByStatusWithSpecAvoidingTopPlacementLimited(
  3171.         City $city,
  3172.         ?Filter $spec,
  3173.         array $additionalSpecs = null,
  3174.         array $genders = [Genders::FEMALE],
  3175.         int $limit = 0,
  3176.     ): array|Page {
  3177.         return $this->profileList->listActiveWithinCityOrderedByStatusWithSpecLimited($city, $spec, $additionalSpecs, $genders, true, $limit);
  3178.     }
  3179.     private function listRandomSinglePage(
  3180.         City $city,
  3181.         ?string $country,
  3182.         ?Filter $spec,
  3183.         ?array $additionalSpecs,
  3184.         bool $active,
  3185.         ?bool $masseur = false,
  3186.         array $genders = [Genders::FEMALE]
  3187.     ): Page {
  3188.         return $this->profileList->listRandom($city, $country, $spec, $additionalSpecs, $active, $masseur, $genders, true);
  3189.     }
  3190.     private function shuffleProfilesOnPage(Page $result): Page
  3191.     {
  3192.         $profiles = iterator_to_array($result->getIterator());
  3193.         if(count($profiles) > 1) {
  3194.             shuffle($profiles);
  3195.         }
  3196.         return new FakeORMQueryPage(
  3197.             $result->getCurrentOffset(),
  3198.             $result->getCurrentPage(),
  3199.             $result->getCurrentLimit(),
  3200.             $result->totalCount(),
  3201.             $profiles
  3202.         );
  3203.     }
  3204.     /**
  3205.      * Достает из списка анкет их id с учетом совместимости разных форматов данных
  3206.      */
  3207.     private function extractProfileIds(array $profiles): array
  3208.     {
  3209.         $ids = array_map(static function ($item) {
  3210.             /**
  3211.              * - array - данные из микросервиса ротации через API
  3212.              * - Profile::getId() - полноценная сущность анкеты
  3213.              * - ProfileListingReadModel::$id - read-model анкеты
  3214.              */
  3215.             return is_array($item) ? $item['id'] : ($item?->id ?? $item?->getId());
  3216.         }, $profiles);
  3217.         return array_filter($ids); // remove null values
  3218.     }
  3219. //    protected function getJSONResponse(array $parameters)
  3220. //    {
  3221. //        $request = $this->request;
  3222. //        $data = json_decode($request->getContent(), true);
  3223. //
  3224. //        $imageSize = !empty($data['imageSize']) ? $data['imageSize'] : "357x500";
  3225. //
  3226. //        /** @var FakeORMQueryPage $queryPage */
  3227. //        $queryPage = $parameters['profiles'];
  3228. //
  3229. //        $profiles = array_map(function(ProfileListingReadModel $profile) use ($imageSize) {
  3230. //            $profile->stations = array_values($profile->stations);
  3231. //            $profile->avatar['path'] = $this->responsiveAssetsService->getResponsiveImageUrl($profile->avatar['path'], 'profile_media', $imageSize, 'jpg');
  3232. //            $profile->uri = $this->generateUrl('profile_preview.page', ['city' => $profile->city->uriIdentity, 'profile' => $profile->uriIdentity]);
  3233. //            return $profile;
  3234. //        }, $queryPage->getArray());
  3235. //
  3236. //        return new JsonResponse([
  3237. //            'profiles' => $profiles,
  3238. //            'currentPage' => $queryPage->getCurrentPage(),
  3239. //        ], Response::HTTP_OK);
  3240. //    }
  3241. }