src/Aqarmap/Bundle/SearchBundle/Services/CompoundSearchService.php line 121
<?phpnamespace Aqarmap\Bundle\SearchBundle\Services;use Aqarmap\Bundle\ListingBundle\Constant\CompoundFeedback;use Aqarmap\Bundle\ListingBundle\Constant\ListingCategories;use Aqarmap\Bundle\ListingBundle\Constant\ListingSections;use Aqarmap\Bundle\ListingBundle\Constant\ListingStatus;use Aqarmap\Bundle\ListingBundle\Entity\CompoundLocation;use Aqarmap\Bundle\MainBundle\Constant\CustomParagraphPlaceTypes;use Aqarmap\Bundle\MainBundle\Entity\CustomParagraph;use Aqarmap\Bundle\MainBundle\Repository\CustomParagraphRepository;use Aqarmap\Bundle\SearchBundle\Repositories\ListingSearchRepository;use Doctrine\ORM\NonUniqueResultException;use Elastica\Aggregation\Terms;use Elastica\Aggregation\ValueCount;use Symfony\Component\Form\FormInterface;use Symfony\Component\HttpFoundation\Request;class CompoundSearchService extends BaseSearchService{/** @var ListingSearchRepository */protected $repository;public const TOTAL_LISTINGS_PER_PAGE = 30;public const CUSTOM_PARAGRAPH_MAX_RESULTS = 1;public const CRITERIA_DATA_TYPE = ['compoundLocations' => 'is_numeric','propertyTypes' => 'is_numeric','priceLevels' => 'is_numeric','compoundStatus' => 'is_numeric','paymentMethods' => 'is_numeric','keywords' => 'is_numeric','finishTypes' => 'is_string','deliveryYears' => 'is_numeric','developerExperience' => 'is_string',];public const DELIVERY_YEAR_FIELD = 'childrenAttributesList.year-built.keyword';public function setSort($sortBy = '_score', $order = 'asc'): void{$this->repository->setSort($sortBy);$this->repository->setOrder($order);}/*** Deep Search, Search inside sub (Locations & Property Types).*/public function enableDeepSearch(array &$criteria): void{try {$this->enableCompoundLocationDeepSearch($criteria);} catch (\Exception) {}}/*** Enable Compound Location Deep Search.*/public function enableCompoundLocationDeepSearch(array &$criteria): void{if (isset($criteria['compoundLocations'])) {$criteria['compoundLocations'] = \is_array($criteria['compoundLocations']) ? $criteria['compoundLocations'] : [$criteria['compoundLocations']];$locations = [];foreach ($criteria['compoundLocations'] as $location) {if (!\is_object($location)) {$location = $this->entityManager->getReference(CompoundLocation::class, $location);}$locations = array_merge($locations, $this->entityManager->getRepository(CompoundLocation::class)->getCompoundLocationChildren($location));}array_walk($locations, function(&$location): void {$location = $location->getId();});$criteria['compoundLocations'] = array_unique($locations);}}/*** Enable property type Deep Search.*/public function enablePropertytypeDeepSearch(array &$criteria): void{if (isset($criteria['propertyTypes'])) {$criteria['propertyTypes'] = \is_array($criteria['propertyTypes']) ? $criteria['propertyTypes'] : [$criteria['propertyTypes']];$propertyTypes = [];foreach ($criteria['propertyTypes'] as $propertyType) {if (!\is_object($propertyType)) {$propertyType = $this->entityManager->getReference(\Aqarmap\Bundle\ListingBundle\Entity\PropertyType::class, $propertyType);}$propertyTypes = array_merge($propertyTypes, $this->entityManager->getRepository(\Aqarmap\Bundle\ListingBundle\Entity\PropertyType::class)->getPropertyTypeChildren($propertyType));}array_walk($propertyTypes, function(&$propertyType): void {$propertyType = $propertyType->getId();});$criteria['propertyTypes'] = array_unique($propertyTypes);}}/*** Handles criteria & find compounds results.** @return \Knp\Component\Pager\Pagination\PaginationInterface|mixed*/public function getCompounds(array $criteria, $page){$criteria = array_merge($criteria, ['category' => ListingCategories::PROJECTS,'nullParent' => null,'status' => ListingStatus::LIVE,]);$this->handleCriteriaToQuery($criteria);return $this->getResults($page, $criteria);}/*** Compounds query.** @param int $page** @return \Knp\Component\Pager\Pagination\PaginationInterface|mixed*/public function getResults($page, array $criteria){$limit = (int) ($criteria['limit'] ?? self::TOTAL_LISTINGS_PER_PAGE) ?: self::TOTAL_LISTINGS_PER_PAGE;$resultsQuery = $this->repository->getResultQuery();// Adding Default Sorting By Featuring$resultsQuery->addSort([$criteria['sort'][0] ?? 'featured' => ['order' => $criteria['direction'] ?? 'desc']]);$results = $this->finder->createPaginatorAdapter($resultsQuery);try {$pagination = $this->paginator->paginate($results, (int) $page ?: 1, $limit);} catch (\Exception) {$pagination = $this->paginator->paginate([], (int) $page ?: 1, $limit);}return $pagination;}/*** Handles criteria and find compounds count.** @return array** @throws \Exception*/public function getResultsCount(Request $request){$fields = explode(',', $request->query->get('aggregation'));$request->query->remove('aggregation');$criteria = $this->compoundService->stringCriteriaToInteger($request->query->all());$criteria = $this->handleCriteriaDataType($criteria);$criteria = array_merge($criteria, ['category' => ListingCategories::PROJECTS,'nullParent' => null,'status' => ListingStatus::LIVE,]);$compoundsCount = [];$oldCriteria = [];foreach ($fields as $field) {$clonedField = $field;$aggregationType = $this->getAggregationType($clonedField);$this->handleRelationField($field);$this->excludeAggregationFromCriteria($clonedField, $criteria, $oldCriteria);$counts = $this->findCompoundsCountByField($field, $criteria, $aggregationType);if (self::DELIVERY_YEAR_FIELD == $field) {$compoundsCount[$clonedField] = $this->handleCountResults($counts, $aggregationType, true);} else {$compoundsCount[$clonedField] = $this->handleCountResults($counts, $aggregationType);}$criteria = array_merge($criteria, $oldCriteria);}$compoundsCount['totalHits'] = $this->findCompoundsTotalCount($criteria);return $compoundsCount;}public function isNoIndexMetaTag(array $request): bool{return isset($request['priceLevels']) || isset($request['compoundStatus'])|| isset($request['paymentMethods']) || isset($request['finishTypes'])|| isset($request['compoundLocations']) || isset($request['propertyTypes'])|| isset($request['keywords']) ? true : false;}/*** Compounds count query.** @return array|mixed*/protected function findCompoundsCountByField(string $field, array $criteria, $aggregationType = CompoundFeedback::AGGERGATION_BUCKET_COUNT_TYPE){$this->handleCriteriaToQuery($criteria);$resultsQuery = $this->repository->getResultQuery();if (CompoundFeedback::AGGERGATION_SINGLE_COUNT_TYPE == $aggregationType) {$aggregation = new ValueCount('fields', $field);} else {$aggregation = new Terms('fields');$aggregation->setField($field);$aggregation->setSize(CompoundFeedback::AGGERGATION_SIZE);}$resultsQuery->addAggregation($aggregation);return $this->finder->createPaginatorAdapter($resultsQuery)->getAggregations();}/*** Compounds total count query.** @return int*/protected function findCompoundsTotalCount(array $criteria){$this->handleCriteriaToQuery($criteria);$resultsQuery = $this->repository->getResultQuery();return $this->finder->createPaginatorAdapter($resultsQuery)->getTotalHits();}/*** Excludes aggregation field from criteria.*/protected function excludeAggregationFromCriteria(string $field, array &$criteria, array &$oldCriteria): void{$aggregationField = $field.'s';if (\array_key_exists($field, $criteria)) {$oldCriteria[$field] = $criteria[$field];unset($criteria[$field]);} elseif (\array_key_exists($aggregationField, $criteria)) {$oldCriteria[$aggregationField] = $criteria[$aggregationField];unset($criteria[$aggregationField]);}}/*** Adds 'id' to relation field.*/protected function handleRelationField(&$field): void{switch ($field) {case 'compoundLocation':$field .= '.id';break;case 'propertyType':$field = 'childrenPropertyType.id';break;case 'finishType':$field = 'childrenAttributesList.finish-type.keyword';break;case 'deliveryYears':$field = self::DELIVERY_YEAR_FIELD;break;case 'hasDelivered':$field = 'compoundField.numberOfProjectsDelivered';break;case 'hasInhabited':$field = 'compoundField.numberOfProjectsInhabited';break;default:break;}}/*** Handles criteria data types.*/protected function handleCriteriaDataType(array $criteria){// TODO implement better valiadtion$acceptedCriteria = self::CRITERIA_DATA_TYPE;foreach ($criteria as $key => $value) {if (\array_key_exists($key, $acceptedCriteria)) {$criteria[$key] = array_filter($value, $acceptedCriteria[$key]);if (empty($criteria[$key])) {unset($criteria[$key]);}}}return $criteria;}/*** Converts criteria value to string.*/protected function handleStringCriteria(&$criteria): void{if (\array_key_exists('keywords', $criteria)) {$criteria['keywords'] = \is_array($criteria['keywords']) ? implode(',', $criteria['keywords']) : $criteria['keywords'];}}/*** handle count results.*/protected function handleCountResults(array $aggregations, ?string $aggregationType = null, $isDeliveryYear = false){if (CompoundFeedback::AGGERGATION_SINGLE_COUNT_TYPE == $aggregationType) {return $aggregations['fields']['value'];}return $this->handleAggregationResults($aggregations, $isDeliveryYear);}/*** Mapping aggregations results.** @return array*/protected function handleAggregationResults(array $aggregations, $isDeliveryYear = false){$result = [];if (\array_key_exists('fields', $aggregations) && \array_key_exists('buckets', $aggregations['fields'])) {foreach ($aggregations['fields']['buckets'] as $aggregation) {if ($isDeliveryYear) {$result[(int) $aggregation['key'] - date('Y')] = $aggregation['doc_count'];} else {$result[$aggregation['key']] = $aggregation['doc_count'];}}}return $result;}/*** Sets Repository.** @throws \Exception*/protected function setRepository(): void{$this->repository = new ListingSearchRepository($this->clientService->getClient(), $this->setting);}/*** Add Location to checked Compound Locations.** @param array $checkedLocations** @return array*/public function addCheckedLocations($checkedLocations, CompoundLocation $location){if (!\in_array($location->getId(), $checkedLocations)) {array_unshift($checkedLocations, $location->getId());}return $checkedLocations;}/*** Add Location to Compound Locations criteria.** @return array*/public function addLocationsToCriteria(array $criteria, CompoundLocation $location){$locationId = $location->getId();if (!isset($criteria['compoundLocations'])) {$criteria['compoundLocations'] = [$locationId];} elseif (!\in_array($locationId, $criteria['compoundLocations'])) {array_unshift($criteria['compoundLocations'], $locationId);}return $criteria;}/*** remove Current Location from Criteria.** @return string*/public function removeCurrentLocation(array $criteria){$locationsIds = $criteria['compoundLocations'];array_shift($locationsIds);return implode(',', $locationsIds);}/*** Generate the Parameters of No Slug in Url of Compound Search for RedirectUrl.** @return array*/public function getRedirectUrlParams(array $criteria){$location = $this->entityManager->getRepository(CompoundLocation::class)->findOneById($criteria['compoundLocations'][0]);if ($location) {$params['location'] = $location->getSlug();}$params['compoundLocations'] = null;if (\count($criteria['compoundLocations']) > 1) {$params['compoundLocations'] = $this->removeCurrentLocation($criteria);}return $params;}/*** Sets compound search data.*/public function setCompoundSearchFormData(FormInterface $form, array $criteria){$compoundService = $this->compoundService;$form->get('priceLevel')->setData($compoundService->getParameter($criteria, 'priceLevels'));$form->get('compoundStatus')->setData($compoundService->getParameter($criteria, 'compoundStatus'));$form->get('paymentMethod')->setData($compoundService->getParameter($criteria, 'paymentMethods'));$form->get('finishType')->setData($compoundService->getParameter($criteria, 'finishType'));$form->get('keyword')->setData($compoundService->getParameter($criteria, 'keywords'));$form->get('finishType')->setData($compoundService->getParameter($criteria, 'finishTypes'));$form->get('deliveryYears')->setData($compoundService->getParameter($criteria, 'deliveryYears'));return $form;}private function getAggregationType(string $field): string{if (\in_array($field, CompoundFeedback::SINGLE_AGGREGATION_FIELDS)) {return CompoundFeedback::AGGERGATION_SINGLE_COUNT_TYPE;}return CompoundFeedback::AGGERGATION_BUCKET_COUNT_TYPE;}/*** @throws \Exception*/public function getCustomParagraph(array $criteria): ?CustomParagraph{$customParagraphs = null;$selectedCustomParagraphId = $criteria['selectedParagraphId'] ?? null;$criteria = $this->getCriteriaCustomParagraph($criteria);$customParagraphRepository = $this->getCustomParagraphRepository();$this->getTranslatableContainer()->setTranslationFallback(false);if ($selectedCustomParagraphId) {$criteria['id'] = $selectedCustomParagraphId;$customParagraphs = $this->getSelectedCustomParagraph($customParagraphRepository, $criteria);}if (!$customParagraphs) {$customParagraphs = $customParagraphRepository->get(array_merge($criteria, ['published' => true]))->getQuery()->getResult();}if (!empty($customParagraphs)) {$customParagraphs = $customParagraphs[array_rand($customParagraphs)];} else {$customParagraphs = null;}return $customParagraphs;}private function getCriteriaCustomParagraph($criteria): array{return ['place' => CustomParagraphPlaceTypes::COMPOUND_PLANNER,'section' => ListingSections::PROJECTS,'propertyType' => $criteria['propertyTypes'] ?? null,'compoundLocations' => $criteria['selectedCompoundLocation'] ?? null,];}private function getCustomParagraphRepository(): CustomParagraphRepository{/* @var CustomParagraphRepository $customParagraphRepository */return $this->entityManager->getRepository(CustomParagraph::class);}private function getSelectedCustomParagraph($customParagraphRepository, $criteria){$result = null;try {$result = $customParagraphRepository->get($criteria)->getQuery()->setMaxResults(self::CUSTOM_PARAGRAPH_MAX_RESULTS)->getOneOrNullResult();} catch (NonUniqueResultException) {$result = null;}return $result;}/*** @return \Gedmo\Translatable\TranslatableListener|object|null** @throws \Exception*/private function getTranslatableContainer(){return $this->translatableListener;}}