src/Controller/ProfileListController.php line 2389

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