src/Service/ProfileListingRecommendationsFiller.php line 43

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 App\Repository\ReadModel\ProfileListingReadModel;
  9. use App\Specification\ElasticSearch\ISpecification;
  10. use Porpaginas\Page;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  13. class ProfileListingRecommendationsFiller
  14. {
  15.     private int $perPage;
  16.     public function __construct(
  17.         private ProfileRecommendationService $profileRecommendationService,
  18.         private ProfileList $profileList,
  19.         private LoggerInterface $logger,
  20.         ParameterBagInterface $parameterBag,
  21.     ) {
  22.         $this->perPage = (int)$parameterBag->get('profile_list.per_page');
  23.     }
  24.     public function fill(City $cityPage $profiles, ?ISpecification $recommendationSpec, array $genders = [Genders::FEMALE]): array
  25.     {
  26.         if (!$this->shouldFill($profiles)) {
  27.             return [
  28.                 'profiles' => $profiles,
  29.                 'applied' => false,
  30.                 'original_count' => $profiles->totalCount(),
  31.             ];
  32.         }
  33.         $currentProfiles iterator_to_array($profiles->getIterator());
  34.         $excludeProfileIds $this->extractProfileIds($currentProfiles);
  35.         $neededCount $this->perPage count($currentProfiles);
  36.         $recommendedListingProfiles $this->loadRecommendedListingProfiles($city$recommendationSpec$genders$excludeProfileIds$neededCount);
  37.         if (empty($recommendedListingProfiles)) {
  38.             return [
  39.                 'profiles' => $profiles,
  40.                 'applied' => false,
  41.                 'original_count' => $profiles->totalCount(),
  42.             ];
  43.         }
  44.         $filledProfiles array_merge($currentProfiles$recommendedListingProfiles);
  45.         $originalCount $profiles->totalCount();
  46.         return [
  47.             'profiles' => new FakeORMQueryPage(
  48.                 $profiles->getCurrentOffset(),
  49.                 $profiles->getCurrentPage(),
  50.                 $profiles->getCurrentLimit(),
  51.                 $originalCount count($recommendedListingProfiles),
  52.                 $filledProfiles,
  53.             ),
  54.             'applied' => true,
  55.             'original_count' => $originalCount,
  56.         ];
  57.     }
  58.     public function recommendedProfilesForPage(City $cityPage $items, ?ISpecification $recommendationSpec, array $genders = [Genders::FEMALE]): array
  59.     {
  60.         if (!$this->shouldFill($items)) {
  61.             return [
  62.                 'profiles' => [],
  63.                 'applied' => false,
  64.                 'original_count' => $items->totalCount(),
  65.             ];
  66.         }
  67.         $recommendedListingProfiles $this->loadRecommendedListingProfiles(
  68.             $city,
  69.             $recommendationSpec,
  70.             $genders,
  71.             [],
  72.             $this->perPage $items->count(),
  73.         );
  74.         return [
  75.             'profiles' => $recommendedListingProfiles,
  76.             'applied' => !empty($recommendedListingProfiles),
  77.             'original_count' => $items->totalCount(),
  78.         ];
  79.     }
  80.     private function shouldFill(Page $profiles): bool
  81.     {
  82.         if ($profiles->count() >= $this->perPage) {
  83.             return false;
  84.         }
  85.         if (=== $profiles->totalCount()) {
  86.             return === $profiles->getCurrentPage();
  87.         }
  88.         return $profiles->getCurrentOffset() < $profiles->totalCount();
  89.     }
  90.     private function extractProfileIds(array $profiles): array
  91.     {
  92.         $ids = [];
  93.         foreach ($profiles as $profile) {
  94.             if ($profile instanceof Profile) {
  95.                 $ids[] = $profile->getId();
  96.                 continue;
  97.             }
  98.             if (isset($profile->id) && is_numeric($profile->id)) {
  99.                 $ids[] = (int)$profile->id;
  100.             }
  101.         }
  102.         return array_values(array_unique($ids));
  103.     }
  104.     private function resolveGender(array $genders): int
  105.     {
  106.         $gender reset($genders);
  107.         return is_numeric($gender) ? (int)$gender Genders::FEMALE;
  108.     }
  109.     private function loadRecommendedListingProfiles(
  110.         City $city,
  111.         ?ISpecification $recommendationSpec,
  112.         array $genders,
  113.         array $excludeProfileIds,
  114.         int $neededCount,
  115.     ): array {
  116.         if ($neededCount <= 0) {
  117.             return [];
  118.         }
  119.         try {
  120.             $recommendedProfiles $this->profileRecommendationService->getRecommendations(
  121.                 $city,
  122.                 $recommendationSpec?->getSpec()->toEsQueryObject(),
  123.                 [],
  124.                 [],
  125.                 $excludeProfileIds,
  126.                 $neededCount,
  127.                 $this->resolveGender($genders),
  128.             );
  129.         } catch (\Throwable $exception) {
  130.             $this->logger->warning('Failed to fill listing with profile recommendations.', [
  131.                 'city_id' => $city->getId(),
  132.                 'exception' => $exception,
  133.             ]);
  134.             return [];
  135.         }
  136.         $recommendedProfileIds array_map(
  137.             static fn(Profile $profile): int => $profile->getId(),
  138.             $recommendedProfiles,
  139.         );
  140.         return $this->profileList->getProfilesByIdsInOrder($recommendedProfileIds);
  141.     }
  142. }