src/Controller/ProfileListController.php line 2805

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