src/Service/ProfileListingRecommendationsFiller.php line 134

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Service;
  4. use App\Bridge\Porpaginas\Doctrine\ORM\FakeORMQueryPage;
  5. use App\Entity\Location\City;
  6. use App\Entity\Profile\Genders;
  7. use App\Entity\Profile\Profile;
  8. use Porpaginas\Page;
  9. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  10. class ProfileListingRecommendationsFiller
  11. {
  12.     private int $perPage;
  13.     public function __construct(
  14.         private ColdStartProfileRecommendationsCache $coldStartProfileRecommendationsCache,
  15.         private ProfileList $profileList,
  16.         ParameterBagInterface $parameterBag,
  17.     ) {
  18.         $this->perPage = (int)$parameterBag->get('profile_list.per_page');
  19.     }
  20.     public function fill(
  21.         City $city,
  22.         Page $profiles,
  23.         array $genders = [Genders::FEMALE],
  24.     ): array
  25.     {
  26.         if (!$this->shouldFill($profiles)) {
  27.             $currentProfiles iterator_to_array($profiles->getIterator());
  28.             return [
  29.                 'profiles' => $profiles,
  30.                 'applied' => false,
  31.                 'original_count' => $profiles->totalCount(),
  32.                 'filled_profile_ids' => [],
  33.                 'visible_profile_ids' => $this->extractProfileIds($currentProfiles),
  34.             ];
  35.         }
  36.         $currentProfiles iterator_to_array($profiles->getIterator());
  37.         $excludeProfileIds $this->extractProfileIds($currentProfiles);
  38.         $neededCount $this->perPage count($currentProfiles);
  39.         $recommendedListingProfiles $this->loadPopularCityProfiles(
  40.             $city,
  41.             $genders,
  42.             $excludeProfileIds,
  43.             $neededCount,
  44.         );
  45.         if (empty($recommendedListingProfiles)) {
  46.             return [
  47.                 'profiles' => $profiles,
  48.                 'applied' => false,
  49.                 'original_count' => $profiles->totalCount(),
  50.                 'filled_profile_ids' => [],
  51.                 'visible_profile_ids' => $excludeProfileIds,
  52.             ];
  53.         }
  54.         $filledProfiles array_merge($currentProfiles$recommendedListingProfiles);
  55.         $originalCount $profiles->totalCount();
  56.         $filledProfileIds $this->extractProfileIds($recommendedListingProfiles);
  57.         return [
  58.             'profiles' => new FakeORMQueryPage(
  59.                 $profiles->getCurrentOffset(),
  60.                 $profiles->getCurrentPage(),
  61.                 $profiles->getCurrentLimit(),
  62.                 $originalCount count($recommendedListingProfiles),
  63.                 $filledProfiles,
  64.             ),
  65.             'applied' => true,
  66.             'original_count' => $originalCount,
  67.             'filled_profile_ids' => $filledProfileIds,
  68.             'visible_profile_ids' => array_values(array_unique(array_merge($excludeProfileIds$filledProfileIds))),
  69.         ];
  70.     }
  71.     public function recommendedProfilesForPage(
  72.         City $city,
  73.         Page $items,
  74.         array $genders = [Genders::FEMALE],
  75.     ): array
  76.     {
  77.         if (!$this->shouldFill($items)) {
  78.             return [
  79.                 'profiles' => [],
  80.                 'applied' => false,
  81.                 'original_count' => $items->totalCount(),
  82.             ];
  83.         }
  84.         $recommendedListingProfiles $this->loadPopularCityProfiles(
  85.             $city,
  86.             $genders,
  87.             [],
  88.             $this->perPage $items->count(),
  89.         );
  90.         return [
  91.             'profiles' => $recommendedListingProfiles,
  92.             'applied' => !empty($recommendedListingProfiles),
  93.             'original_count' => $items->totalCount(),
  94.         ];
  95.     }
  96.     public function popularProfilesForBlock(
  97.         City $city,
  98.         Page $items,
  99.         array $genders = [Genders::FEMALE],
  100.         int $count 5,
  101.         array $excludeProfileIds = [],
  102.     ): array
  103.     {
  104.         $excludeProfileIds array_values(array_unique(array_merge(
  105.             $excludeProfileIds,
  106.             $this->extractProfileIds(iterator_to_array($items->getIterator())),
  107.         )));
  108.         return $this->loadPopularCityProfiles(
  109.             $city,
  110.             $genders,
  111.             $excludeProfileIds,
  112.             $count,
  113.         );
  114.     }
  115.     private function shouldFill(Page $profiles): bool
  116.     {
  117.         if ($profiles->count() >= $this->perPage) {
  118.             return false;
  119.         }
  120.         if (=== $profiles->totalCount()) {
  121.             return === $profiles->getCurrentPage();
  122.         }
  123.         return $profiles->getCurrentOffset() < $profiles->totalCount();
  124.     }
  125.     private function extractProfileIds(array $profiles): array
  126.     {
  127.         $ids = [];
  128.         foreach ($profiles as $profile) {
  129.             if ($profile instanceof Profile) {
  130.                 $ids[] = $profile->getId();
  131.                 continue;
  132.             }
  133.             if (isset($profile->id) && is_numeric($profile->id)) {
  134.                 $ids[] = (int)$profile->id;
  135.                 continue;
  136.             }
  137.             if (is_array($profile) && isset($profile['id']) && is_numeric($profile['id'])) {
  138.                 $ids[] = (int)$profile['id'];
  139.             }
  140.         }
  141.         return array_values(array_unique($ids));
  142.     }
  143.     private function resolveGender(array $genders): int
  144.     {
  145.         $gender reset($genders);
  146.         return is_numeric($gender) ? (int)$gender Genders::FEMALE;
  147.     }
  148.     private function loadPopularCityProfiles(
  149.         City $city,
  150.         array $genders,
  151.         array $excludeProfileIds,
  152.         int $neededCount,
  153.     ): array {
  154.         if ($neededCount <= 0) {
  155.             return [];
  156.         }
  157.         $recommendedProfileIds $this->coldStartProfileRecommendationsCache->getProfileIds(
  158.             $city,
  159.             $this->resolveGender($genders),
  160.             $this->recommendationCandidateLimit($neededCount),
  161.             $excludeProfileIds,
  162.         );
  163.         return array_slice(
  164.             $this->profileList->getActiveProfilesByIdsInOrderWithinCityAndSpec(
  165.                 $city,
  166.                 $recommendedProfileIds,
  167.                 null,
  168.                 null,
  169.                 $genders,
  170.             ),
  171.             0,
  172.             $neededCount,
  173.         );
  174.     }
  175.     private function recommendationCandidateLimit(int $neededCount): int
  176.     {
  177.         return min($this->perPagemax($neededCount$neededCount 4));
  178.     }
  179. }