src/Controller/ProfileListController.php line 244

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