src/Controller/ProfileListController.php line 936

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