src/Controller/ProfileListController.php line 253

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