src/Aqarmap/Bundle/SearchBundle/Services/ElasticListingSearch/AbstractElasticListingSearch.php line 118

Open in your IDE?
  1. <?php
  2. namespace Aqarmap\Bundle\SearchBundle\Services\ElasticListingSearch;
  3. use Aqarmap\Bundle\ListingBundle\Constant\ListingCategories;
  4. use Aqarmap\Bundle\ListingBundle\Entity\Listing;
  5. use Aqarmap\Bundle\ListingBundle\Entity\ListingSearchScoreSetting;
  6. use Aqarmap\Bundle\ListingBundle\Entity\Location;
  7. use Aqarmap\Bundle\ListingBundle\Entity\PropertyType;
  8. use Aqarmap\Bundle\ListingBundle\Service\ListingManager;
  9. use Aqarmap\Bundle\MainBundle\Service\Setting;
  10. use Aqarmap\Bundle\SearchBundle\Repositories\ListingSearchRepository;
  11. use Aqarmap\Bundle\SearchBundle\Services\ElasticClientService;
  12. use Doctrine\ORM\EntityManagerInterface;
  13. use FOS\ElasticaBundle\Finder\TransformedFinder;
  14. use FOS\ElasticaBundle\HybridResult;
  15. use Knp\Component\Pager\Paginator;
  16. use Knp\Component\Pager\PaginatorInterface;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\HttpFoundation\RequestStack;
  19. use Symfony\Component\HttpFoundation\Session\Session;
  20. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  21. use Symfony\Component\Routing\RouterInterface;
  22. use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
  23. use Symfony\Contracts\Translation\TranslatorInterface;
  24. abstract class AbstractElasticListingSearch
  25. {
  26.     public const TOTAL_LISTINGS_PER_PAGE 30;
  27.     /**
  28.      * @var ListingSearchRepository
  29.      */
  30.     protected $repository;
  31.     /**
  32.      * @var ElasticClientService
  33.      */
  34.     protected $elasticClientService;
  35.     /**
  36.      * @var EntityManagerInterface
  37.      */
  38.     protected $entityManager;
  39.     /**
  40.      * @var Paginator
  41.      */
  42.     protected $paginator;
  43.     /**
  44.      * @var TransformedFinder
  45.      */
  46.     protected $finder;
  47.     /**
  48.      * @var Session
  49.      */
  50.     protected $session;
  51.     /**
  52.      * @var RouterInterface
  53.      */
  54.     protected $router;
  55.     /**
  56.      * @var Setting
  57.      */
  58.     protected $setting;
  59.     /**
  60.      * @var ListingManager
  61.      */
  62.     protected $listingManager;
  63.     /**
  64.      * @var Translator
  65.      */
  66.     protected $translator;
  67.     /**
  68.      * @var array
  69.      */
  70.     private $parentsToChildrenLocationsMap = [];
  71.     /**
  72.      * @var RequestStack
  73.      */
  74.     private $requestStack;
  75.     public function __construct(
  76.         SessionInterface $session,
  77.         EntityManagerInterface $entityManager,
  78.         PaginatorInterface $paginator,
  79.         ElasticClientService $elasticClientService,
  80.         TransformedFinder $finder,
  81.         RequestStack $requestStack,
  82.         RouterInterface $router,
  83.         Setting $setting,
  84.         TokenStorageInterface $tokenStorage,
  85.         ListingManager $listingManager,
  86.         TranslatorInterface $translator
  87.     ) {
  88.         $this->session $session;
  89.         $this->entityManager $entityManager;
  90.         $this->paginator $paginator;
  91.         $this->elasticClientService $elasticClientService;
  92.         $this->finder $finder;
  93.         $this->requestStack $requestStack;
  94.         $this->router $router;
  95.         $this->setting $setting;
  96.         $this->listingManager $listingManager;
  97.         $this->translator $translator;
  98.         $this->setRepository();
  99.         $this->setRequest($requestStack);
  100.     }
  101.     final public function search(array $criteria)
  102.     {
  103.         $criteria $this->buildCriteria($criteria);
  104.         $this->enableDeepSearch($criteria);
  105.         $this->handleCriteriaToQuery($criteria);
  106.         return $this->getResults((int) $criteria['page'], (int) $criteria['limit']);
  107.     }
  108.     /**
  109.      * Get the search criteria locations mapped to their children.
  110.      *
  111.      * @return array
  112.      */
  113.     public function getParentsToChildrenLocationsMap()
  114.     {
  115.         return $this->parentsToChildrenLocationsMap;
  116.     }
  117.     abstract protected function getResults(int $page 1int $limit 20);
  118.     protected function setSort($sortBy '_score'$order 'asc'): void
  119.     {
  120.         $this->repository->setSort($sortBy);
  121.         $this->repository->setOrder($order);
  122.     }
  123.     /**
  124.      * Deep Search, Search inside sub (Locations & Property Types).
  125.      */
  126.     protected function enableDeepSearch(array &$criteria): void
  127.     {
  128.         $this->enableLocationDeepSearch($criteria);
  129.         $this->enablePropertyTypeDeepSearch($criteria);
  130.     }
  131.     /**
  132.      * @return array
  133.      */
  134.     protected function getScoreSettings()
  135.     {
  136.         $settingsRaw $this->entityManager->getRepository(ListingSearchScoreSetting::class)->findAll();
  137.         $settings = [];
  138.         foreach ($settingsRaw as $setting) {
  139.             $settings[$setting->getName()] = $setting->getValue();
  140.         }
  141.         return $settings;
  142.     }
  143.     protected function setRepository(): void
  144.     {
  145.         $this->repository = new ListingSearchRepository($this->elasticClientService->getClient(), $this->setting);
  146.     }
  147.     /**
  148.      * call handler for each criteria type.
  149.      *
  150.      * @param array $criteria
  151.      */
  152.     protected function handleCriteriaToQuery($criteria): void
  153.     {
  154.         foreach ($criteria as $key => $value) {
  155.             $keyUp ucfirst($key);
  156.             if (method_exists($this->repository'handle'.$keyUp)) {
  157.                 \call_user_func([$this->repository'handle'.$keyUp], $value);
  158.             }
  159.         }
  160.     }
  161.     protected function getQuery()
  162.     {
  163.         return $this->repository->getResultQuery();
  164.     }
  165.     /**
  166.      * Set array of listing Scoring.
  167.      *
  168.      * @param array $listings
  169.      */
  170.     protected function setListingsScoring($listings = []): array
  171.     {
  172.         $items = [];
  173.         foreach ($listings as $listing) {
  174.             $items[] = $this->setListingScoring($listing);
  175.         }
  176.         return $items;
  177.     }
  178.     /**
  179.      * Set Listing Scoring.
  180.      *
  181.      * @return Listing
  182.      */
  183.     private function setListingScoring(HybridResult &$listing)
  184.     {
  185.         $score $listing->getResult()->getScore();
  186.         $listing $listing->getTransformed();
  187.         $listing->setElasticSearchScore($score);
  188.         return $listing;
  189.     }
  190.     private function setRequest(RequestStack $requestStack): self
  191.     {
  192.         if (!$requestStack->getCurrentRequest()) {
  193.             $requestStack->push(new Request());
  194.         }
  195.         return $this;
  196.     }
  197.     /**
  198.      * @param array $criteria
  199.      *
  200.      * @return array
  201.      */
  202.     private function buildCriteria($criteria)
  203.     {
  204.         $limit = (int) $this->setting->getSetting('features''listings_count_per_search_page') ?: self::TOTAL_LISTINGS_PER_PAGE;
  205.         $criteria['limit'] ??= $limit;
  206.         $criteria['page'] = $this->requestStack->getCurrentRequest()->query->get('page'1);
  207.         $criteria['excludedCategory'] = !$this->setting->getSetting('features''listing_search_scrapped') ?
  208.             [ListingCategories::SCRAPPED] :
  209.             [];
  210.         return $criteria;
  211.     }
  212.     /**
  213.      * Enable Location Deep Search.
  214.      */
  215.     private function enableLocationDeepSearch(array &$criteria): void
  216.     {
  217.         if (isset($criteria['location'])) {
  218.             $locations = [];
  219.             $criteria['location'] = !\is_array($criteria['location']) ? [$criteria['location']] : $criteria['location'];
  220.             foreach ($criteria['location'] as $location) {
  221.                 if (!$location instanceof Location) {
  222.                     $location $this->entityManager->getReference('AqarmapListingBundle:Location'$location);
  223.                 }
  224.                 $childrenLocations $this
  225.                     ->entityManager
  226.                     ->getRepository(Location::class)
  227.                     ->getLocationChildrenIds($location);
  228.                 $this->parentsToChildrenLocationsMap[$location->getId()] = $childrenLocations;
  229.                 $locations array_merge(
  230.                     $locations,
  231.                     $childrenLocations
  232.                 );
  233.             }
  234.             $criteria['location'] = $locations;
  235.         }
  236.     }
  237.     /**
  238.      * Enable PropertyType Deep Search.
  239.      */
  240.     private function enablePropertyTypeDeepSearch(array &$criteria): void
  241.     {
  242.         if (isset($criteria['propertyType'])) {
  243.             if (!\is_object($criteria['propertyType'])) {
  244.                 $criteria['propertyType'] = $this->entityManager->getRepository(PropertyType::class)->findOneBy([
  245.                     'id' => (int) $criteria['propertyType'],
  246.                 ]);
  247.             }
  248.             $criteria['propertyType'] = $this
  249.                 ->entityManager
  250.                 ->getRepository(PropertyType::class)
  251.                 ->getPropertyTypeChildrenIds($criteria['propertyType']);
  252.         }
  253.     }
  254. }