src/Aqarmap/Bundle/SearchBundle/Controller/Api/SearchResultsController.php line 23

  1. <?php
  2. namespace Aqarmap\Bundle\SearchBundle\Controller\Api;
  3. use Aqarmap\Bundle\ListingBundle\Builder\BuilderInterface;
  4. use Aqarmap\Bundle\ListingBundle\Entity\Location;
  5. use Aqarmap\Bundle\ListingBundle\Entity\PropertyType;
  6. use Aqarmap\Bundle\ListingBundle\Entity\Section;
  7. use Aqarmap\Bundle\ListingBundle\Repository\LocationRepository;
  8. use Aqarmap\Bundle\ListingBundle\Repository\PropertyTypeRepository;
  9. use Aqarmap\Bundle\ListingBundle\Repository\SectionRepository;
  10. use Aqarmap\Bundle\ListingBundle\Service\ListingManager;
  11. use Aqarmap\Bundle\MainBundle\Constant\CustomParagraphPlaceTypes;
  12. use Aqarmap\Bundle\MainBundle\Controller\Api\BaseController;
  13. use Aqarmap\Bundle\MainBundle\Repository\CustomParagraphRepository;
  14. use Aqarmap\Bundle\MainBundle\Service\MenuBuilderService;
  15. use Aqarmap\Bundle\SearchBundle\Services\SEOListingSearchService;
  16. use FOS\RestBundle\Controller\Annotations as Rest;
  17. use Symfony\Component\HttpFoundation\Request;
  18. use Symfony\Component\HttpKernel\Attribute\Cache;
  19. class SearchResultsController extends BaseController
  20. {
  21. #[Rest\Get('/api/v2/search/slug-resolver', options: ['i18n' => false], name: 'aqarmap_api_search_slug_resolver')]
  22. #[Rest\View(serializerGroups: ['SlugResolver'])]
  23. #[Rest\QueryParam(name: 'section', default: null, description: 'Section slug')]
  24. #[Rest\QueryParam(name: 'propertyType', default: null, description: 'Property Type slug')]
  25. #[Rest\QueryParam(name: 'locations', default: null, description: 'Locations slugs. As array.')]
  26. #[Cache(expires: '+7 days', maxage: '+7 days', smaxage: '+7 days', public: true, vary: ['Accept-Language', 'X-Accept-Version', 'Accept'])]
  27. public function resolveSearchPageSlugs(Request $request, SectionRepository $sectionRepo, PropertyTypeRepository $propertyTypeRepo, LocationRepository $locationRepo): array
  28. {
  29. $locationsSlugs = (array) $request->query->get('locations', []);
  30. $data = [
  31. 'section' => $sectionRepo->findOneBy(['slug' => $request->query->get('section')]),
  32. 'propertyType' => $propertyTypeRepo->findOneBy(['slug' => $request->query->get('propertyType')]),
  33. 'locations' => [],
  34. ];
  35. if (!empty($locationsSlugs) && $this->validLocationSlugArray($locationsSlugs)) {
  36. $data['locations'] = $locationRepo->getLocationsBySlugs($locationsSlugs);
  37. }
  38. return $data;
  39. }
  40. /**
  41. * Legazy.
  42. */
  43. #[Rest\Get('/api/v2/search/properties-chips-legazy', options: ['i18n' => false], name: 'aqarmap_api_properties_chips_legazy')]
  44. #[Rest\View]
  45. #[Cache(expires: '+1 days', maxage: '+1 days', smaxage: '+1 days', public: true, vary: ['Accept-Language', 'X-Accept-Version', 'Accept'])]
  46. public function getPropertiesChipsLegazy(Request $request, #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'true', converter: 'querystring', mapping: ['location' => 'id'])]
  47. ?Location $location, #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'true', converter: 'querystring', mapping: ['propertyType' => 'id'])]
  48. ?PropertyType $propertyType, #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'true', converter: 'querystring', mapping: ['section' => 'id'])]
  49. ?Section $section, MenuBuilderService $menuBuilderService)
  50. {
  51. $locale = $request->getLocale();
  52. $data = [];
  53. if ($section && $propertyType) {
  54. $criteria = [
  55. 'location' => $location,
  56. 'propertyType' => $propertyType,
  57. 'section' => $section,
  58. 'locale' => $locale,
  59. ];
  60. $data = $menuBuilderService->getSearchResultPropertyChips($criteria);
  61. }
  62. return $data;
  63. }
  64. #[Rest\Get('/api/v2/search/properties-chips', options: ['i18n' => false], name: 'aqarmap_api_properties_chips')]
  65. #[Rest\View]
  66. #[Cache(expires: '+1 days', maxage: '+1 days', smaxage: '+1 days', public: true, vary: ['Accept-Language', 'X-Accept-Version', 'Accept'])]
  67. public function getPropertiesChips(Request $request, ListingManager $listingManager, PropertyTypeRepository $propertyTypeRepository)
  68. {
  69. $result = [];
  70. $types = $listingManager->getPropertiesChipsElasticResponse($request);
  71. $typesArray = [];
  72. foreach ($types as $id => $doc_count) {
  73. $typesArray[$id] = $doc_count;
  74. }
  75. arsort($typesArray);
  76. $propertyTypes = $propertyTypeRepository->findPropertyTypes(array_keys($typesArray));
  77. foreach ($propertyTypes as $propertyType) {
  78. $id = $propertyType->getId();
  79. $result[] = [
  80. 'id' => $id,
  81. 'title' => $propertyType->getTitle(),
  82. 'slug' => $propertyType->getSlug(),
  83. 'searchResultCount' => $typesArray[$id] ?? 0,
  84. 'level' => $propertyType->getLevel(),
  85. ];
  86. }
  87. return $result;
  88. }
  89. #[Rest\Get('/api/v2/search/{section}/{propertyType}/{location}/page-metadata', options: ['i18n' => false], name: 'aqarmap_api_search_page_metadata')]
  90. #[Rest\View(serializerGroups: ['Default'])]
  91. public function getSeoData(
  92. Section $section,
  93. PropertyType $propertyType,
  94. Location $location,
  95. CustomParagraphRepository $customParagraphRepository,
  96. SEOListingSearchService $seoListingSearchService,
  97. ) {
  98. $data['customParagraph'] = $customParagraphRepository->get([
  99. 'location' => $location->getId(),
  100. 'propertyType' => $propertyType->getId(),
  101. 'section' => $section->getId(),
  102. 'place' => CustomParagraphPlaceTypes::SEARCH_RESULTS,
  103. ])->getQuery()->setMaxResults(1)->getOneOrNullResult();
  104. $data['longTail'] = $seoListingSearchService->retrieveLongTail($location, $section, $propertyType);
  105. return [
  106. 'data' => $data,
  107. ];
  108. }
  109. #[Rest\Get('/api/v2/custom-paragraph', options: ['i18n' => false], name: 'aqarmap_api_custom_paragraph')]
  110. #[Rest\View(serializerGroups: ['Default'])]
  111. public function getCustomParagraph(
  112. Request $request,
  113. #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'true', converter: 'querystring', mapping: ['location' => 'id'])]
  114. ?Location $location,
  115. #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'true', converter: 'querystring', mapping: ['section' => 'id'])]
  116. Section $section,
  117. #[\Symfony\Bridge\Doctrine\Attribute\MapEntity(isOptional: 'false', converter: 'querystring', mapping: ['propertyType' => 'id'])]
  118. ?PropertyType $propertyType,
  119. CustomParagraphRepository $customParagraphRepository,
  120. ) {
  121. return $customParagraphRepository->get([
  122. 'location' => $location ? $location->getId() : null,
  123. 'propertyType' => $propertyType ? $propertyType->getId() : null,
  124. 'section' => $section->getId(),
  125. 'place' => $request->query->get('place') ?: CustomParagraphPlaceTypes::SEARCH_RESULTS,
  126. ])->getQuery()->setMaxResults(1)->getOneOrNullResult();
  127. }
  128. #[Rest\Get('/api/v2/location/{location}/listing', options: ['i18n' => false], name: 'aqarmap_api_location_listing_v2')]
  129. #[Rest\QueryParam(name: 'id', default: null, description: 'Location id')]
  130. #[Rest\View(serializerGroups: ['locationListingV2'])]
  131. public function getLocationListing(Location $location)
  132. {
  133. return $location;
  134. }
  135. private function validLocationSlugArray($array): bool
  136. {
  137. return \count($array) > 0 && array_reduce($array, fn ($carry, $item) => $carry && \is_string($item) && '' !== $item, true);
  138. }
  139. #[Rest\Get('/api/v4/search/ssr-data', options: ['i18n' => false, 'expose' => false], name: 'aqarmap_search_results_ssr_data')]
  140. #[Rest\QueryParam(name: 'section', default: null, description: 'Section slug')]
  141. #[Rest\QueryParam(name: 'propertyType', default: null, description: 'Property Type slug')]
  142. #[Rest\QueryParam(name: 'locations', default: null, description: 'Array of Locations slugs.')]
  143. #[Rest\View(serializerGroups: ['SlugResolver'])]
  144. public function singlePageSSRData(Request $request, SectionRepository $sectionRepository, PropertyTypeRepository $propertyTypeRepository, LocationRepository $locationRepository, BuilderInterface $searchDescriptionBuilder): array
  145. {
  146. $sectionSlug = $request->get('section');
  147. if (!$sectionSlug) {
  148. throw new \InvalidArgumentException('Section is required');
  149. }
  150. $section = $sectionRepository->findOneBy(['slug' => $sectionSlug]);
  151. $propertyTypeSlug = $request->get('propertyType');
  152. if (!$propertyTypeSlug) {
  153. throw new \InvalidArgumentException('propertyType is required');
  154. }
  155. $propertyType = $propertyTypeRepository->findOneBy(['slug' => $propertyTypeSlug]);
  156. $locationsSlugs = (array) $request->get('locations', []);
  157. if (!$locationsSlugs) {
  158. throw new \InvalidArgumentException('locations is required');
  159. }
  160. $locations = $locationRepository->getLocationsBySlugs($locationsSlugs);
  161. // Page Description
  162. $searchDescriptionBuilder
  163. ->setSection($section)
  164. ->setPropertyType($propertyType)
  165. ->setLocation(reset($locations))
  166. ;
  167. return [
  168. 'section' => $section,
  169. 'propertyType' => $propertyType,
  170. 'locations' => $locations,
  171. 'pageTitle' => '',
  172. 'metaDescription' => $searchDescriptionBuilder->format()->get(),
  173. 'metaKeywords' => '',
  174. 'canonicalUrl' => '',
  175. 'breadcrumbs' => [
  176. [
  177. 'title' => '',
  178. 'url' => '',
  179. ],
  180. ],
  181. 'propertyTypesChips' => [
  182. [
  183. 'title' => '',
  184. 'url' => '',
  185. ],
  186. ],
  187. ];
  188. }
  189. }