src/Service/ProfileListSpecificationService.php line 692

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. use App\Dto\ProfileListSpecification;
  4. use App\Entity\Location\City;
  5. use App\Entity\Location\County;
  6. use App\Entity\Location\District;
  7. use App\Entity\Location\Station;
  8. use App\Entity\Profile\Appearances;
  9. use App\Entity\Profile\BodyTypes;
  10. use App\Entity\Profile\BreastTypes;
  11. use App\Entity\Profile\Genders;
  12. use App\Entity\Profile\HairColors;
  13. use App\Entity\Profile\Nationalities;
  14. use App\Entity\Profile\ProfileInventoryItem;
  15. use App\Entity\Profile\PrivateHaircuts;
  16. use App\Entity\Service;
  17. use App\Entity\ServiceGroups;
  18. use App\Entity\TakeOutLocations;
  19. use App\Repository\DistrictRepository;
  20. use App\Repository\ProfileRepository;
  21. use Doctrine\ORM\EntityManagerInterface;
  22. use App\Repository\ServiceRepository;
  23. use App\Specification\ElasticSearch\ISpecification;
  24. use App\Specification\Profile\BdsmProfile;
  25. use App\Specification\Profile\ProfileAdBoardPlacement;
  26. use App\Specification\Profile\ProfileHasApartments;
  27. use App\Specification\Profile\ProfileHasComments;
  28. use App\Specification\Profile\ProfileHasHighPrice;
  29. use App\Specification\Profile\ProfileHasInventoryItem;
  30. use App\Specification\Profile\ProfileHasLowPrice;
  31. use App\Specification\Profile\ProfileHasOnCall;
  32. use App\Specification\Profile\ProfileHasOutcallOrLocations;
  33. use App\Specification\Profile\ProfileHasOneOfGenders;
  34. use App\Specification\Profile\ProfileHasPriceGreaterThan;
  35. use App\Specification\Profile\ProfileHasDistrict;
  36. use App\Specification\Profile\ProfileHasProvidedServices;
  37. use App\Specification\Profile\ProfileHasSelfie;
  38. use App\Specification\Profile\ProfileHasStations;
  39. use App\Specification\Profile\ProfileHasVideo;
  40. use App\Specification\Profile\ProfileInMoskowDistricts;
  41. use App\Specification\Profile\ProfileIsActive;
  42. use App\Specification\Profile\ProfileIsApproved;
  43. use App\Specification\Profile\ProfileIsElite;
  44. use App\Specification\Profile\ProfileIsForHour;
  45. use App\Specification\Profile\ProfileIsForNight;
  46. use App\Specification\Profile\ProfileIsGirlfriend;
  47. use App\Specification\Profile\ProfileIsIndividual;
  48. use App\Specification\Profile\ProfileInCityTopPercent;
  49. use App\Specification\Profile\ProfileIsLocated;
  50. use App\Specification\Profile\ProfileIsMasseur;
  51. use App\Specification\Profile\ProfileIsMostExpensive;
  52. use App\Specification\Profile\ProfileIsNew;
  53. use App\Specification\Profile\ProfileIsNice;
  54. use App\Specification\Profile\ProfileIsNotMasseur;
  55. use App\Specification\Profile\ProfileIsNotExpensive;
  56. use App\Specification\Profile\ProfileIsNotHidden;
  57. use App\Specification\Profile\ProfileIsNotLocatedInCity;
  58. use App\Specification\Profile\ProfileIsProvidingOneOfServices;
  59. use App\Specification\Profile\ProfileIsProvidingTakeOut;
  60. use App\Specification\Profile\ProfileIsRealElite;
  61. use App\Specification\Profile\ProfileIsUpdatedWithinAYear;
  62. use App\Specification\Profile\ProfileIsWithoutPrepayment;
  63. use App\Specification\Profile\ProfileHasWhatsapp;
  64. use App\Specification\Profile\ProfileHasTelegram;
  65. use App\Specification\Profile\ProfileHasMax;
  66. use App\Specification\Profile\ProfileIsEighteenYearsOld;
  67. use App\Specification\Profile\ProfileIsVip;
  68. use App\Specification\Profile\ProfileIsVipIndividual;
  69. use App\Specification\Profile\ProfileIsVipMoskow;
  70. use App\Specification\Profile\ProfileNoRetouch;
  71. use App\Specification\Profile\ProfileOwnerHasAtMostProfiles;
  72. use App\Specification\Profile\ProfilePriceThresholds;
  73. use App\Specification\Profile\ProfileWithAge;
  74. use App\Specification\Profile\ProfileWithAppearance;
  75. use App\Specification\Profile\ProfileWithApartmentsOneHourPrice;
  76. use App\Specification\Profile\ProfileWithBodyType;
  77. use App\Specification\Profile\ProfileWithBreastType;
  78. use App\Specification\Profile\ProfileWithHairColor;
  79. use App\Specification\Profile\ProfileWithHeight;
  80. use App\Specification\Profile\ProfileWithNationality;
  81. use App\Specification\Profile\ProfileWithPrivateHaircut;
  82. use App\Specification\Profile\ProfileWithTakeOutOneHourPrice;
  83. use App\Specification\QueryModifier\LimitResult;
  84. use App\Specification\QueryModifier\ProfileAvatar;
  85. use App\Specification\QueryModifier\ProfileOrderedByNightPrice;
  86. use App\Specification\QueryModifier\ProfilePlacementHiding;
  87. use Happyr\DoctrineSpecification\Filter\Filter;
  88. use Happyr\DoctrineSpecification\Logic\AndX;
  89. use Happyr\DoctrineSpecification\Spec;
  90. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  91. class ProfileListSpecificationService
  92. {
  93.     public function __construct(
  94.         private CountryCurrencyResolver $countryCurrencyResolver,
  95.         private ServiceRepository $serviceRepository,
  96.         private ParameterBagInterface $parameterBag,
  97.         private ProfileRepository $profileRepository,
  98.         private EntityManagerInterface $entityManager,
  99.         private DistrictRepository $districtRepository,
  100.         private Features $features,
  101.         private ProfileIndividuality $profileIndividuality,
  102.         private ProfileLandingDataService $profileLandingDataService,
  103.     ) {}
  104.     public function listForMasseur(City $city): ProfileListSpecification
  105.     {
  106.         return new ProfileListSpecification(new ProfileIsMasseur(), \App\Specification\ElasticSearch\ProfileIsLocated::withinCity($city));
  107.     }
  108.     public function listByCity(): ProfileListSpecification
  109.     {
  110.         return new ProfileListSpecification(null, null);
  111.     }
  112.     public function listIndiMain(City $city): ProfileListSpecification
  113.     {
  114.         return new ProfileListSpecification(
  115.             new ProfileIsIndividual(
  116.                 $city,
  117.                 $this->features->hard_moderation(),
  118.                 $this->profileIndividuality->includeFallbackProfiles($city)
  119.             ),
  120.             null
  121.         );
  122.     }
  123.     public function listIndiChecked(City $city): ProfileListSpecification
  124.     {
  125.         return new ProfileListSpecification(
  126.             Spec::andX(
  127.                 new ProfileIsIndividual(
  128.                     $city,
  129.                     $this->features->hard_moderation(),
  130.                     $this->profileLandingDataService->includeIndividualFallbackProfiles($city)
  131.                 ),
  132.                 new ProfileIsApproved()
  133.             ),
  134.             null
  135.         );
  136.     }
  137.     public function listIndiMature(City $city): ProfileListSpecification
  138.     {
  139.         return new ProfileListSpecification(
  140.             $this->profileInCityTopPercent(
  141.                 $city,
  142.                 ProfileInCityTopPercent::CATEGORY_INDIVIDUAL,
  143.                 ProfileInCityTopPercent::METRIC_AGE
  144.             ),
  145.             null
  146.         );
  147.     }
  148.     public function listIndiCheap(City $city): ProfileListSpecification
  149.     {
  150.         return new ProfileListSpecification(
  151.             $this->profileInCityTopPercent(
  152.                 $city,
  153.                 ProfileInCityTopPercent::CATEGORY_INDIVIDUAL,
  154.                 ProfileInCityTopPercent::METRIC_PRICE
  155.             ),
  156.             null
  157.         );
  158.     }
  159.     public function listIndiWithComments(City $city): ProfileListSpecification
  160.     {
  161.         return new ProfileListSpecification(
  162.             Spec::andX(
  163.                 new ProfileIsIndividual(
  164.                     $city,
  165.                     $this->features->hard_moderation(),
  166.                     $this->profileLandingDataService->includeIndividualFallbackProfiles($city)
  167.                 ),
  168.                 new ProfileHasComments()
  169.             ),
  170.             null
  171.         );
  172.     }
  173.     public function listMassagersWithoutIntermediaries(): ProfileListSpecification
  174.     {
  175.         return new ProfileListSpecification(
  176.             Spec::andX(
  177.                 new ProfileIsProvidingOneOfServices($this->getMassageGroupServices()),
  178.                 new ProfileOwnerHasAtMostProfiles(3)
  179.             ),
  180.             null
  181.         );
  182.     }
  183.     public function listMassagersMature(City $city): ProfileListSpecification
  184.     {
  185.         return new ProfileListSpecification(
  186.             $this->profileInCityTopPercent(
  187.                 $city,
  188.                 ProfileInCityTopPercent::CATEGORY_MASSAGER,
  189.                 ProfileInCityTopPercent::METRIC_AGE
  190.             ),
  191.             null
  192.         );
  193.     }
  194.     public function listMassagersCheap(City $city): ProfileListSpecification
  195.     {
  196.         return new ProfileListSpecification(
  197.             $this->profileInCityTopPercent(
  198.                 $city,
  199.                 ProfileInCityTopPercent::CATEGORY_MASSAGER,
  200.                 ProfileInCityTopPercent::METRIC_PRICE
  201.             ),
  202.             null
  203.         );
  204.     }
  205.     private function profileInCityTopPercent(City $city, string $category, string $metric): ProfileInCityTopPercent
  206.     {
  207.         return new ProfileInCityTopPercent(
  208.             $this->profileLandingDataService->topPercentProfileIds($city, $category, $metric),
  209.             $category,
  210.             $metric,
  211.         );
  212.     }
  213.     public function listMassagersOutcall(): ProfileListSpecification
  214.     {
  215.         return new ProfileListSpecification(
  216.             Spec::andX(
  217.                 new ProfileIsProvidingOneOfServices($this->getMassageGroupServices()),
  218.                 new ProfileHasOutcallOrLocations()
  219.             ),
  220.             null
  221.         );
  222.     }
  223.     public function listPagesWithStations(): ProfileListSpecification
  224.     {
  225.         return new ProfileListSpecification(new ProfileHasStations(), null);
  226.     }
  227.     public function listPagesWithDistricts(): ProfileListSpecification
  228.     {
  229.         return new ProfileListSpecification(new ProfileHasDistrict(), null);
  230.     }
  231.     public function listPagesWithSuburbs(City $defaultCity): ProfileListSpecification
  232.     {
  233.         return new ProfileListSpecification(new ProfileIsNotLocatedInCity($defaultCity), null);
  234.     }
  235.     public function listPagesWithProvidedServices(): ProfileListSpecification
  236.     {
  237.         return new ProfileListSpecification(new ProfileHasProvidedServices(), null);
  238.     }
  239.     public function listByCounty(County $county): ProfileListSpecification
  240.     {
  241.         return new ProfileListSpecification(ProfileIsLocated::withinCounty($county), \App\Specification\ElasticSearch\ProfileIsLocated::withinCounty($county));
  242.     }
  243.     public function listByCountyEromassage(County $county): ProfileListSpecification
  244.     {
  245.         $massageServices = $this->getMassageGroupServices();
  246.         return new ProfileListSpecification(
  247.             Spec::andX(
  248.                 ProfileIsLocated::withinCounty($county),
  249.                 new ProfileIsProvidingOneOfServices($massageServices)
  250.             ),
  251.             \App\Specification\ElasticSearch\Spec::andX(
  252.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinCounty($county),
  253.                 new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($massageServices)
  254.             )
  255.         );
  256.     }
  257.     public function listByCountyVerified(County $county): ProfileListSpecification
  258.     {
  259.         return new ProfileListSpecification(
  260.             Spec::andX(
  261.                 ProfileIsLocated::withinCounty($county),
  262.                 new ProfileIsApproved()
  263.             ),
  264.             \App\Specification\ElasticSearch\Spec::andX(
  265.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinCounty($county),
  266.                 new \App\Specification\ElasticSearch\ProfileIsApproved()
  267.             )
  268.         );
  269.     }
  270.     public function listByCountyCheap(County $county): ProfileListSpecification
  271.     {
  272.         return new ProfileListSpecification(
  273.             Spec::andX(
  274.                 ProfileIsLocated::withinCounty($county),
  275.                 $this->lowPriceSpec($county->getCity())
  276.             ),
  277.             null
  278.         );
  279.     }
  280.     public function listByCountyMature(County $county): ProfileListSpecification
  281.     {
  282.         return new ProfileListSpecification(
  283.             Spec::andX(
  284.                 ProfileIsLocated::withinCounty($county),
  285.                 ProfileWithAge::old()
  286.             ),
  287.             null
  288.         );
  289.     }
  290.     public function listByCountyUzbek(County $county): ProfileListSpecification
  291.     {
  292.         return new ProfileListSpecification(
  293.             Spec::andX(
  294.                 ProfileIsLocated::withinCounty($county),
  295.                 new ProfileWithNationality(Nationalities::UZBEK)
  296.             ),
  297.             \App\Specification\ElasticSearch\Spec::andX(
  298.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinCounty($county),
  299.                 new \App\Specification\ElasticSearch\ProfileWithNationality(Nationalities::UZBEK)
  300.             )
  301.         );
  302.     }
  303.     public function listByDistrict(District $district): ProfileListSpecification
  304.     {
  305.         return new ProfileListSpecification(ProfileIsLocated::withinDistrict($district), \App\Specification\ElasticSearch\ProfileIsLocated::withinDistrict($district));
  306.     }
  307.     public function listByDistrictEromassage(District $district): ProfileListSpecification
  308.     {
  309.         $massageServices = $this->getMassageGroupServices();
  310.         return new ProfileListSpecification(
  311.             Spec::andX(
  312.                 ProfileIsLocated::withinDistrict($district),
  313.                 new ProfileIsProvidingOneOfServices($massageServices)
  314.             ),
  315.             \App\Specification\ElasticSearch\Spec::andX(
  316.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinDistrict($district),
  317.                 new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($massageServices)
  318.             )
  319.         );
  320.     }
  321.     public function listByDistrictVerified(District $district): ProfileListSpecification
  322.     {
  323.         return new ProfileListSpecification(
  324.             Spec::andX(
  325.                 ProfileIsLocated::withinDistrict($district),
  326.                 new ProfileIsApproved()
  327.             ),
  328.             \App\Specification\ElasticSearch\Spec::andX(
  329.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinDistrict($district),
  330.                 new \App\Specification\ElasticSearch\ProfileIsApproved()
  331.             )
  332.         );
  333.     }
  334.     public function listByDistrictCheap(District $district): ProfileListSpecification
  335.     {
  336.         return new ProfileListSpecification(
  337.             Spec::andX(
  338.                 ProfileIsLocated::withinDistrict($district),
  339.                 $this->lowPriceSpec($district->getCity())
  340.             ),
  341.             null
  342.         );
  343.     }
  344.     public function listByDistrictMature(District $district): ProfileListSpecification
  345.     {
  346.         return new ProfileListSpecification(
  347.             Spec::andX(
  348.                 ProfileIsLocated::withinDistrict($district),
  349.                 ProfileWithAge::old()
  350.             ),
  351.             null
  352.         );
  353.     }
  354.     public function listByDistrictUzbek(District $district): ProfileListSpecification
  355.     {
  356.         return new ProfileListSpecification(
  357.             Spec::andX(
  358.                 ProfileIsLocated::withinDistrict($district),
  359.                 new ProfileWithNationality(Nationalities::UZBEK)
  360.             ),
  361.             \App\Specification\ElasticSearch\Spec::andX(
  362.                 \App\Specification\ElasticSearch\ProfileIsLocated::withinDistrict($district),
  363.                 new \App\Specification\ElasticSearch\ProfileWithNationality(Nationalities::UZBEK)
  364.             )
  365.         );
  366.     }
  367.     public function listByStation(Station $station): ProfileListSpecification
  368.     {
  369.         return new ProfileListSpecification(ProfileIsLocated::nearStation($station), \App\Specification\ElasticSearch\ProfileIsLocated::nearStations([$station]));
  370.     }
  371.     public function listByStationEromassage(Station $station): ProfileListSpecification
  372.     {
  373.         $massageServices = $this->getMassageGroupServices();
  374.         return new ProfileListSpecification(
  375.             Spec::andX(
  376.                 ProfileIsLocated::nearStation($station),
  377.                 new ProfileIsProvidingOneOfServices($massageServices)
  378.             ),
  379.             \App\Specification\ElasticSearch\Spec::andX(
  380.                 \App\Specification\ElasticSearch\ProfileIsLocated::nearStations([$station]),
  381.                 new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($massageServices)
  382.             )
  383.         );
  384.     }
  385.     public function listByStationVerified(Station $station): ProfileListSpecification
  386.     {
  387.         return new ProfileListSpecification(
  388.             Spec::andX(
  389.                 ProfileIsLocated::nearStation($station),
  390.                 new ProfileIsApproved()
  391.             ),
  392.             \App\Specification\ElasticSearch\Spec::andX(
  393.                 \App\Specification\ElasticSearch\ProfileIsLocated::nearStations([$station]),
  394.                 new \App\Specification\ElasticSearch\ProfileIsApproved()
  395.             )
  396.         );
  397.     }
  398.     public function listByStationCheap(Station $station): ProfileListSpecification
  399.     {
  400.         return new ProfileListSpecification(
  401.             Spec::andX(
  402.                 ProfileIsLocated::nearStation($station),
  403.                 $this->lowPriceSpec($station->getCity())
  404.             ),
  405.             null
  406.         );
  407.     }
  408.     public function listByStationMature(Station $station): ProfileListSpecification
  409.     {
  410.         return new ProfileListSpecification(
  411.             Spec::andX(
  412.                 ProfileIsLocated::nearStation($station),
  413.                 ProfileWithAge::old()
  414.             ),
  415.             null
  416.         );
  417.     }
  418.     public function listByStationUzbek(Station $station): ProfileListSpecification
  419.     {
  420.         return new ProfileListSpecification(
  421.             Spec::andX(
  422.                 ProfileIsLocated::nearStation($station),
  423.                 new ProfileWithNationality(Nationalities::UZBEK)
  424.             ),
  425.             \App\Specification\ElasticSearch\Spec::andX(
  426.                 \App\Specification\ElasticSearch\ProfileIsLocated::nearStations([$station]),
  427.                 new \App\Specification\ElasticSearch\ProfileWithNationality(Nationalities::UZBEK)
  428.             )
  429.         );
  430.     }
  431.     public function listByStations(array $stations): ProfileListSpecification
  432.     {
  433.         //        $stationIds = explode(',', $stations);
  434.         //        $stations = $this->stationRepository->findBy(['uriIdentity' => $stationIds]);
  435.         return new ProfileListSpecification(new ProfileIsLocated(null, null, null, $stations), \App\Specification\ElasticSearch\ProfileIsLocated::nearStations($stations));
  436.     }
  437.     public function listApproved(): ProfileListSpecification
  438.     {
  439.         return new ProfileListSpecification(new ProfileIsApproved(), new \App\Specification\ElasticSearch\ProfileIsApproved());
  440.     }
  441.     public function listWithWhatsapp(): ProfileListSpecification
  442.     {
  443.         return new ProfileListSpecification(
  444.             new ProfileHasWhatsapp(),
  445.             new \App\Specification\ElasticSearch\ProfileHasWhatsapp()
  446.         );
  447.     }
  448.     public function listWithTelegram(): ProfileListSpecification
  449.     {
  450.         return new ProfileListSpecification(
  451.             new ProfileHasTelegram(),
  452.             new \App\Specification\ElasticSearch\ProfileHasTelegram()
  453.         );
  454.     }
  455.     public function listWithMax(): ProfileListSpecification
  456.     {
  457.         return new ProfileListSpecification(
  458.             new ProfileHasMax(),
  459.             new \App\Specification\ElasticSearch\ProfileHasMax()
  460.         );
  461.     }
  462.     public function listEighteenYearsOld(): ProfileListSpecification
  463.     {
  464.         return new ProfileListSpecification(
  465.             new ProfileIsEighteenYearsOld(),
  466.             new \App\Specification\ElasticSearch\ProfileIsEighteenYearsOld()
  467.         );
  468.     }
  469.     public function listRublevskie(City $city): ProfileListSpecification
  470.     {
  471.         $stationRepo = $this->entityManager->getRepository(\App\Entity\Location\Station::class);
  472.         $stationUris = require __DIR__ . '/../../config/rublevskie_stations.php';
  473.         $stations = $stationRepo->createQueryBuilder('s')
  474.             ->andWhere('s.city = :city')
  475.             ->andWhere('s.uriIdentity IN (:uris)')
  476.             ->setParameter('city', $city)
  477.             ->setParameter('uris', $stationUris)
  478.             ->getQuery()->getResult();
  479.         return new ProfileListSpecification(
  480.             new \App\Specification\Profile\ProfileIsRublevskie($city, $this->entityManager),
  481.             new \App\Specification\ElasticSearch\ProfileIsRublevskie($city, $stations)
  482.         );
  483.     }
  484.     public function listWithTattoo(): ProfileListSpecification
  485.     {
  486.         return new ProfileListSpecification(
  487.             new \App\Specification\Profile\ProfileIsHavingTattoo(true),
  488.             new \App\Specification\ElasticSearch\ProfileIsHavingTattoo(true)
  489.         );
  490.     }
  491.     public function listWithPiercing(): ProfileListSpecification
  492.     {
  493.         return new ProfileListSpecification(
  494.             new \App\Specification\Profile\ProfileIsHavingPiercing(true),
  495.             new \App\Specification\ElasticSearch\ProfileIsHavingPiercing(true)
  496.         );
  497.     }
  498.     public function listRoundTheClock(): ProfileListSpecification
  499.     {
  500.         return new ProfileListSpecification(
  501.             new \App\Specification\Profile\ProfileIsRoundTheClock(),
  502.             new \App\Specification\ElasticSearch\ProfileIsRoundTheClock()
  503.         );
  504.     }
  505.     public function listForTwoHours(): ProfileListSpecification
  506.     {
  507.         return new ProfileListSpecification(
  508.             new \App\Specification\Profile\ProfileIsForTwoHours(),
  509.             new \App\Specification\ElasticSearch\ProfileIsForTwoHours()
  510.         );
  511.     }
  512.     public function listGrandmothers(): ProfileListSpecification
  513.     {
  514.         return new ProfileListSpecification(
  515.             new \App\Specification\Profile\ProfileIsGrandmother(),
  516.             new \App\Specification\ElasticSearch\ProfileIsGrandmother()
  517.         );
  518.     }
  519.     public function listWithComments(): ProfileListSpecification
  520.     {
  521.         return new ProfileListSpecification(new ProfileHasComments(), new \App\Specification\ElasticSearch\ProfileHasComments());
  522.     }
  523.     public function listWithVideo(): ProfileListSpecification
  524.     {
  525.         return new ProfileListSpecification(new ProfileHasVideo(), new \App\Specification\ElasticSearch\ProfileHasVideo());
  526.     }
  527.     public function listWithSelfie(): ProfileListSpecification
  528.     {
  529.         return new ProfileListSpecification(new ProfileHasSelfie(), new \App\Specification\ElasticSearch\ProfileHasSelfie());
  530.     }
  531.     public function listByPrice(City $city, string $priceType, int $minPrice = null, int $maxPrice = null): ProfileListSpecification
  532.     {
  533.         $currency = $this->countryCurrencyResolver->getCurrencyFor($city->getCountryCode());
  534.         if ($minPrice && $maxPrice) {
  535.             $priceSpec = ProfileWithApartmentsOneHourPrice::range($minPrice, $maxPrice);
  536.             $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithApartmentsOneHourPrice::range($minPrice, $maxPrice);
  537.         } elseif ($maxPrice) {
  538.             $priceSpec = Spec::andX(
  539.                 new ProfileIsNotMasseur(),
  540.                 Spec::orX(
  541.                     ProfileWithApartmentsOneHourPrice::cheaperThan($maxPrice),
  542.                     new ProfileWithTakeOutOneHourPrice(null, $maxPrice)
  543.                 )
  544.             );
  545.             $recommendationSpec = \App\Specification\ElasticSearch\Spec::andX(
  546.                 new \App\Specification\ElasticSearch\ProfileHasMasseurStatus(false),
  547.                 \App\Specification\ElasticSearch\Spec::orX(
  548.                     \App\Specification\ElasticSearch\ProfileWithApartmentsOneHourPrice::cheaperThan($maxPrice),
  549.                     \App\Specification\ElasticSearch\ProfileWithTakeOutOneHourPrice::cheaperThan($maxPrice)
  550.                 )
  551.             );
  552.         } else {
  553.             switch ($priceType) {
  554.                 case 'not_expensive':
  555.                     $priceSpec = new ProfileIsNotExpensive($city);
  556.                     $recommendationSpec = new \App\Specification\ElasticSearch\ProfileIsNotExpensive($currency);
  557.                     break;
  558.                 case 'low':
  559.                     $priceSpec = new ProfileHasLowPrice($city, $currency, false);
  560.                     $recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasLowPrice($city, $currency);
  561.                     break;
  562.                 case 'elite':
  563.                     $eliteMinPrice = (new ProfilePriceThresholds($city->getCityPriceCategory(), $currency))->eliteMinPrice();
  564.                     $eliteMinPrices = null === $eliteMinPrice ? [] : [$currency => $eliteMinPrice];
  565.                     $priceSpec = new ProfileHasHighPrice($city, $currency, $eliteMinPrices, [], false);
  566.                     $recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasHighPrice($city, $currency, $eliteMinPrices, []);
  567.                     break;
  568.                 case 'high':
  569.                 default:
  570.                     $priceSpec = new ProfileHasHighPrice($city, $currency, null, null, false);
  571.                     $recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasHighPrice($city, $currency);
  572.                     break;
  573.             }
  574.         }
  575.         return new ProfileListSpecification($priceSpec, $recommendationSpec);
  576.     }
  577.     public function listByAge(string $ageType, int $minAge = null, int $maxAge = null): ProfileListSpecification
  578.     {
  579.         if ($minAge && $maxAge) {
  580.             $ageSpec = ProfileWithAge::range($minAge, $maxAge);
  581.             $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::range($minAge, $maxAge);
  582.         } elseif ($minAge) {
  583.             $ageSpec = ProfileWithAge::olderThan($minAge);
  584.             $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::olderThan($minAge);
  585.         } else {
  586.             switch ($ageType) {
  587.                 case 'old':
  588.                     $ageSpec = ProfileWithAge::old();
  589.                     $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::old();
  590.                     break;
  591.                 case 'young':
  592.                 default:
  593.                     $ageSpec = ProfileWithAge::young();
  594.                     $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithAge::young();
  595.                     break;
  596.             }
  597.         }
  598.         return new ProfileListSpecification($ageSpec, $recommendationSpec);
  599.     }
  600.     public function listByHeight(string $heightType): ProfileListSpecification
  601.     {
  602.         if ('short' === $heightType) {
  603.             $heightSpec = ProfileWithHeight::short();
  604.             $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithHeight::short();
  605.         } else {
  606.             $heightSpec = ProfileWithHeight::tall();
  607.             $recommendationSpec = \App\Specification\ElasticSearch\ProfileWithHeight::tall();
  608.         }
  609.         return new ProfileListSpecification($heightSpec, $recommendationSpec);
  610.     }
  611.     public function listByBreastType(string $breastType): ProfileListSpecification
  612.     {
  613.         $breastTypeSpec = new ProfileWithBreastType(BreastTypes::getValueByUriIdentity($breastType));
  614.         return new ProfileListSpecification($breastTypeSpec, new \App\Specification\ElasticSearch\ProfileWithBreastType(BreastTypes::getValueByUriIdentity($breastType)));
  615.     }
  616.     public function listByHairColor(string $hairColor): ProfileListSpecification
  617.     {
  618.         $hairColorSpec = new ProfileWithHairColor(HairColors::getValueByUriIdentity($hairColor));
  619.         return new ProfileListSpecification($hairColorSpec, new \App\Specification\ElasticSearch\ProfileWithHairColor(HairColors::getValueByUriIdentity($hairColor)));
  620.     }
  621.     public function listByBodyType(string $bodyType): ProfileListSpecification
  622.     {
  623.         $bodyTypeSpec = new ProfileWithBodyType(BodyTypes::getValueByUriIdentity($bodyType));
  624.         return new ProfileListSpecification($bodyTypeSpec, new \App\Specification\ElasticSearch\ProfileWithBodyType(BodyTypes::getValueByUriIdentity($bodyType)));
  625.     }
  626.     public function listByPlace(string $placeType, string $takeOutLocation = null): ?ProfileListSpecification
  627.     {
  628.         switch ($placeType) {
  629.             case 'apartments':
  630.                 $placeSpec = new ProfileHasApartments();
  631.                 $recommendationSpec = new \App\Specification\ElasticSearch\ProfileHasApartments();
  632.                 break;
  633.             case 'take-out':
  634.                 $takeOutLocation = TakeOutLocations::getValueByUriIdentity($takeOutLocation);
  635.                 $placeSpec = new ProfileIsProvidingTakeOut($takeOutLocation);
  636.                 $recommendationSpec = new \App\Specification\ElasticSearch\ProfileIsProvidingTakeOut($takeOutLocation);
  637.                 break;
  638.             case 'on-call':
  639.                 $placeSpec = Spec::andX(
  640.                     new ProfileIsProvidingTakeOut(null),
  641.                     Spec::not(new ProfileHasApartments())
  642.                 );
  643.                 $recommendationSpec = \App\Specification\ElasticSearch\Spec::andX(
  644.                     (new \App\Specification\ElasticSearch\ProfileIsProvidingTakeOut(null))->getSpec(),
  645.                     \App\Specification\ElasticSearch\Spec::not((new \App\Specification\ElasticSearch\ProfileHasApartments())->getSpec())
  646.                 );
  647.                 break;
  648.             default:
  649.                 return null;
  650.         }
  651.         return new ProfileListSpecification($placeSpec, $recommendationSpec);
  652.     }
  653.     public function listByPrivateHaircut(string $privateHaircut): ProfileListSpecification
  654.     {
  655.         $privateHaircutSpec = new ProfileWithPrivateHaircut(PrivateHaircuts::getValueByUriIdentity($privateHaircut));
  656.         return new ProfileListSpecification($privateHaircutSpec, new \App\Specification\ElasticSearch\ProfileWithPrivateHaircut(PrivateHaircuts::getValueByUriIdentity($privateHaircut)));
  657.     }
  658.     public function listByNationality(string $nationality): ProfileListSpecification
  659.     {
  660.         $nationalitySpec = new ProfileWithNationality(Nationalities::getValueByUriIdentity($nationality));
  661.         return new ProfileListSpecification($nationalitySpec, new \App\Specification\ElasticSearch\ProfileWithNationality(Nationalities::getValueByUriIdentity($nationality)));
  662.     }
  663.     public function listByAppearance(string $appearance): ProfileListSpecification
  664.     {
  665.         $appearanceSpec = new ProfileWithAppearance(Appearances::getValueByUriIdentity($appearance));
  666.         return new ProfileListSpecification($appearanceSpec, new \App\Specification\ElasticSearch\ProfileWithAppearance(Appearances::getValueByUriIdentity($appearance)));
  667.     }
  668.     public function listByProvidedService(Service $service, City $city): ProfileListSpecification
  669.     {
  670.         $sameGroupServices = $this->serviceRepository->findBy(['group' => $service->getGroup()]);
  671.         $spec = new AndX(new ProfileIsProvidingOneOfServices([$service]));
  672.         if ('eskort' == $service->getUriIdentity() && true === $this->features->escort_additional_requirements()) {
  673.             $spec->andX(new ProfileIsApproved());
  674.             $spec->andX(new ProfileHasPriceGreaterThan($city, 'RUB', [
  675.                 'RUB' => 12000,
  676.             ]));
  677.             $spec->andX(new ProfileIsUpdatedWithinAYear());
  678.         }
  679.         return new ProfileListSpecification($spec, new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($sameGroupServices));
  680.     }
  681.     public function listByInventoryItem(ProfileInventoryItem $item): ProfileListSpecification
  682.     {
  683.         return new ProfileListSpecification(new ProfileHasInventoryItem($item), null);
  684.     }
  685.     public function listNew(int $weeks = 2): ProfileListSpecification
  686.     {
  687.         return new ProfileListSpecification(new ProfileIsNew($weeks), new \App\Specification\ElasticSearch\ProfileIsNew($weeks));
  688.     }
  689.     public function listByNoRetouch(): ProfileListSpecification
  690.     {
  691.         return new ProfileListSpecification(new ProfileNoRetouch(), new \App\Specification\ElasticSearch\ProfileNoRetouch());
  692.     }
  693.     public function listByNice(): ProfileListSpecification
  694.     {
  695.         return new ProfileListSpecification(new ProfileIsNice(), new \App\Specification\ElasticSearch\ProfileIsNice());
  696.     }
  697.     public function listByOnCall(): ProfileListSpecification
  698.     {
  699.         return new ProfileListSpecification(new ProfileHasOnCall(), new \App\Specification\ElasticSearch\ProfileHasOnCall());
  700.     }
  701.     public function listForHour(): ProfileListSpecification
  702.     {
  703.         return new ProfileListSpecification(new ProfileIsForHour(), new \App\Specification\ElasticSearch\ProfileIsForHour());
  704.     }
  705.     public function listForNight(): ProfileListSpecification
  706.     {
  707.         return new ProfileListSpecification(new ProfileIsForNight(), new \App\Specification\ElasticSearch\ProfileIsForNight());
  708.     }
  709.     private function getSpecForEliteGirls(City $city): Filter
  710.     {
  711.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  712.             'RUB' => 5000,
  713.             'UAH' => 1500,
  714.             'USD' => 100,
  715.             'EUR' => 130,
  716.         ]);
  717.         return new ProfileIsElite($minPrice);
  718.     }
  719.     private function getElasticSearchSpecForEliteGirls(City $city): ISpecification
  720.     {
  721.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  722.             'RUB' => 5000,
  723.             'UAH' => 1500,
  724.             'USD' => 100,
  725.             'EUR' => 130,
  726.         ]);
  727.         return new \App\Specification\ElasticSearch\ProfileIsElite($minPrice);
  728.     }
  729.     public function listForEliteGirls(City $city): ProfileListSpecification
  730.     {
  731.         $eliteSpec = $this->getSpecForEliteGirls($city);
  732.         return new ProfileListSpecification($eliteSpec, $this->getElasticSearchSpecForEliteGirls($city));
  733.     }
  734.     public function listForRealElite(City $city): ProfileListSpecification
  735.     {
  736.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  737.             'RUB' => 5000,
  738.             'UAH' => 1500,
  739.             'USD' => 100,
  740.             'EUR' => 130,
  741.         ]);
  742.         return new ProfileListSpecification(new ProfileIsRealElite($minPrice), new \App\Specification\ElasticSearch\ProfileIsRealElite($minPrice));
  743.     }
  744.     public function listForVipPros(City $city): ProfileListSpecification
  745.     {
  746.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  747.             'RUB' => 4000,
  748.             'UAH' => 1200,
  749.             'USD' => 100,
  750.             'EUR' => 100,
  751.         ]);
  752.         $districts = [];
  753.         foreach (ProfileInMoskowDistricts::$districtsUri as $uri) {
  754.             $districts[] = $this->districtRepository->ofUriIdentityWithinCity($uri, $city);
  755.         }
  756.         return new ProfileListSpecification(new ProfileIsVip($city, $districts, $minPrice), new \App\Specification\ElasticSearch\ProfileIsVip($city, $districts, $minPrice));
  757.     }
  758.     public function listForVipIndividual(City $city): ProfileListSpecification
  759.     {
  760.         $minPrice = $this->countryCurrencyResolver->getValueByCountryCode($city->getCountryCode(), [
  761.             'RUB' => 30000,
  762.             'UAH' => 10000,
  763.             'USD' => 400,
  764.             'EUR' => 400,
  765.         ]);
  766.         return new ProfileListSpecification(new ProfileIsVipIndividual($minPrice), new \App\Specification\ElasticSearch\ProfileIsVipIndividual($minPrice));
  767.     }
  768.     public function listForVipGirlsCity(City $city): ProfileListSpecification
  769.     {
  770.         return new ProfileListSpecification(new ProfileIsVipMoskow($city), new \App\Specification\ElasticSearch\ProfileIsVipMoskow($city));
  771.     }
  772.     public function listOfGirlfriends(): ProfileListSpecification
  773.     {
  774.         return new ProfileListSpecification(new ProfileIsGirlfriend(), new \App\Specification\ElasticSearch\ProfileIsGirlfriend());
  775.     }
  776.     public function listOfMostExpensive(City $city): ProfileListSpecification
  777.     {
  778.         $count = intval($this->profileRepository->match(Spec::countOf(Spec::andX(
  779.             ProfileIsLocated::withinCity($city),
  780.             new ProfileHasOneOfGenders([Genders::FEMALE])
  781.         )))[0][1]);
  782.         $criteria = Spec::andX(
  783.             $this->features->free_profiles() ? new ProfileIsNotHidden() : new ProfileIsActive(),
  784.             new ProfileAdBoardPlacement(),
  785.             new ProfilePlacementHiding(),
  786.             new ProfileAvatar(),
  787.             ProfileIsLocated::withinCity($city),
  788.             new LimitResult(round($count / 10), 0),
  789.             new ProfileOrderedByNightPrice(),
  790.             new ProfileHasOneOfGenders([Genders::FEMALE])
  791.         );
  792.         $profilesTenPercents = $this->profileRepository->match($criteria);
  793.         $criteriaProfile = end($profilesTenPercents);
  794.         return new ProfileListSpecification(new ProfileIsMostExpensive($criteriaProfile), new \App\Specification\ElasticSearch\ProfileIsMostExpensive($criteriaProfile));
  795.     }
  796.     public function listBdsm(): ProfileListSpecification
  797.     {
  798.         $bdsmIds = $this->serviceRepository->findBy(['group' => ServiceGroups::BDSM]);
  799.         return new ProfileListSpecification(null, new \App\Specification\ElasticSearch\ProfileIsProvidingOneOfServices($bdsmIds), [new BdsmProfile($this->parameterBag->get('profile_list.bdsm_page.min_profile_services'))]);
  800.     }
  801.     public function listByGender(string $gender): ProfileListSpecification
  802.     {
  803.         $gender = Genders::getValueByUriIdentity($gender);
  804.         $genders = [$gender];
  805.         return new ProfileListSpecification(null, null, null, $genders);
  806.     }
  807.     public function listBigAss(): ProfileListSpecification
  808.     {
  809.         return new ProfileListSpecification(
  810.             new \App\Specification\Profile\ProfileIsBigAss(),
  811.             new \App\Specification\ElasticSearch\ProfileIsBigAss()
  812.         );
  813.     }
  814.     public function listExpress(): ProfileListSpecification
  815.     {
  816.         return new ProfileListSpecification(
  817.             new \App\Specification\Profile\ProfileIsProvidingExpress(),
  818.             new \App\Specification\ElasticSearch\ProfileIsProvidingExpress()
  819.         );
  820.     }
  821.     public function listBigBreast(): ProfileListSpecification
  822.     {
  823.         return new ProfileListSpecification(
  824.             new \App\Specification\Profile\ProfileHasBigBreast(),
  825.             new \App\Specification\ElasticSearch\ProfileHasBigBreast()
  826.         );
  827.     }
  828.     
  829.     public function listVerySkinny(): ProfileListSpecification
  830.     {
  831.         return new ProfileListSpecification(
  832.             new \App\Specification\Profile\ProfileIsVerySkinny(),
  833.             new \App\Specification\ElasticSearch\ProfileIsVerySkinny()
  834.         );
  835.     }
  836.     public function listSmallAss(): ProfileListSpecification
  837.     {
  838.         return new ProfileListSpecification(
  839.             new \App\Specification\Profile\ProfileIsSmallAss(),
  840.             new \App\Specification\ElasticSearch\ProfileIsSmallAss()
  841.         );
  842.     }
  843.     public function listBeautifulProstitutes(): ProfileListSpecification
  844.     {
  845.         return new ProfileListSpecification(
  846.             new \App\Specification\Profile\ProfileIsBeautifulProstitute(),
  847.             new \App\Specification\ElasticSearch\ProfileIsBeautifulProstitute()
  848.         );
  849.     }
  850.         public function listWithoutIntermediaries(): ProfileListSpecification
  851.     {
  852.         return new ProfileListSpecification(
  853.             new \App\Specification\Profile\ProfileIsWithoutIntermediaries(),
  854.             null 
  855.         );
  856.     }
  857.     public function listByOutcall(): ProfileListSpecification
  858.     {
  859.         return new ProfileListSpecification(new \App\Specification\Profile\ProfileHasOutcall(), new \App\Specification\ElasticSearch\ProfileHasOutcall());
  860.     }
  861.     public function listDwarfs(): ProfileListSpecification
  862.     {
  863.         return new ProfileListSpecification(new \App\Specification\Profile\ProfileIsDwarf(), new \App\Specification\ElasticSearch\ProfileIsDwarf());
  864.     }
  865.     public function listArchived(): ProfileListSpecification
  866.     {
  867.         return new ProfileListSpecification(
  868.             new \App\Specification\Profile\ProfileIsArchived(),
  869.             new \App\Specification\ElasticSearch\ProfileIsArchived()
  870.         );
  871.     }
  872.     public function listWithoutPrepayment(): ProfileListSpecification
  873.     {
  874.         return new ProfileListSpecification(
  875.             new \App\Specification\Profile\ProfileWithoutPrepayment(),
  876.             new \App\Specification\ElasticSearch\ProfileWithoutPrepayment()
  877.         );
  878.     }
  879.     private function getMassageGroupServices(): array
  880.     {
  881.         return $this->serviceRepository->findBy(['group' => ServiceGroups::MASSAGE]);
  882.     }
  883.     private function extractStationIds(array $stations): array
  884.     {
  885.         $stationIds = [];
  886.         foreach ($stations as $station) {
  887.             if ($station instanceof Station) {
  888.                 $stationIds[] = $station->getId();
  889.             }
  890.         }
  891.         return $stationIds;
  892.     }
  893.     private function resolveCityCurrency(City $city): string
  894.     {
  895.         return $this->countryCurrencyResolver->getCurrencyFor($city->getCountryCode());
  896.     }
  897.     private function lowPriceSpec(City $city): Filter
  898.     {
  899.         $maxPrice = (new ProfilePriceThresholds(
  900.             $city->getCityPriceCategory(),
  901.             $this->resolveCityCurrency($city)
  902.         ))->lowMaxPrice();
  903.         return Spec::orX(
  904.             Spec::andX(
  905.                 Spec::isNotNull('apartmentsPricing.oneHourPrice'),
  906.                 Spec::lte('apartmentsPricing.oneHourPrice', $maxPrice)
  907.             ),
  908.             Spec::andX(
  909.                 Spec::isNotNull('takeOutPricing.oneHourPrice'),
  910.                 Spec::lte('takeOutPricing.oneHourPrice', $maxPrice)
  911.             )
  912.         );
  913.     }
  914. }