PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/magento/module-catalog-search/Model/Search/RequestGenerator.php

https://bitbucket.org/sergiu-tot-fb/vendors
PHP | 254 lines | 181 code | 15 blank | 58 comment | 11 complexity | ed284d91cf9570ec4f61567261f86cb0 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\CatalogSearch\Model\Search;
  7. use Magento\Catalog\Api\Data\EavAttributeInterface;
  8. use Magento\Catalog\Model\Entity\Attribute;
  9. use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
  10. use Magento\CatalogSearch\Model\Search\RequestGenerator\GeneratorResolver;
  11. use Magento\Framework\App\ObjectManager;
  12. use Magento\Framework\Search\Request\FilterInterface;
  13. use Magento\Framework\Search\Request\QueryInterface;
  14. /**
  15. * @api
  16. * @since 100.0.2
  17. */
  18. class RequestGenerator
  19. {
  20. /** Filter name suffix */
  21. const FILTER_SUFFIX = '_filter';
  22. /** Bucket name suffix */
  23. const BUCKET_SUFFIX = '_bucket';
  24. /**
  25. * @var CollectionFactory
  26. */
  27. private $productAttributeCollectionFactory;
  28. /**
  29. * @var GeneratorResolver
  30. */
  31. private $generatorResolver;
  32. /**
  33. * @param CollectionFactory $productAttributeCollectionFactory
  34. * @param GeneratorResolver $generatorResolver
  35. */
  36. public function __construct(
  37. CollectionFactory $productAttributeCollectionFactory,
  38. GeneratorResolver $generatorResolver = null
  39. ) {
  40. $this->productAttributeCollectionFactory = $productAttributeCollectionFactory;
  41. $this->generatorResolver = $generatorResolver
  42. ?: ObjectManager::getInstance()->get(GeneratorResolver::class);
  43. }
  44. /**
  45. * Generate dynamic fields requests
  46. *
  47. * @return array
  48. */
  49. public function generate()
  50. {
  51. $requests = [];
  52. $requests['catalog_view_container'] =
  53. $this->generateRequest(EavAttributeInterface::IS_FILTERABLE, 'catalog_view_container', false);
  54. $requests['quick_search_container'] =
  55. $this->generateRequest(EavAttributeInterface::IS_FILTERABLE_IN_SEARCH, 'quick_search_container', true);
  56. $requests['advanced_search_container'] = $this->generateAdvancedSearchRequest();
  57. return $requests;
  58. }
  59. /**
  60. * Generate search request
  61. *
  62. * @param string $attributeType
  63. * @param string $container
  64. * @param bool $useFulltext
  65. * @return array
  66. */
  67. private function generateRequest($attributeType, $container, $useFulltext)
  68. {
  69. $request = [];
  70. foreach ($this->getSearchableAttributes() as $attribute) {
  71. if ($attribute->getData($attributeType)) {
  72. if (!in_array($attribute->getAttributeCode(), ['price', 'category_ids'], true)) {
  73. $queryName = $attribute->getAttributeCode() . '_query';
  74. $request['queries'][$container]['queryReference'][] = [
  75. 'clause' => 'must',
  76. 'ref' => $queryName,
  77. ];
  78. $filterName = $attribute->getAttributeCode() . self::FILTER_SUFFIX;
  79. $request['queries'][$queryName] = [
  80. 'name' => $queryName,
  81. 'type' => QueryInterface::TYPE_FILTER,
  82. 'filterReference' => [
  83. [
  84. 'clause' => 'must',
  85. 'ref' => $filterName,
  86. ]
  87. ],
  88. ];
  89. $bucketName = $attribute->getAttributeCode() . self::BUCKET_SUFFIX;
  90. $generator = $this->generatorResolver->getGeneratorForType($attribute->getBackendType());
  91. $request['filters'][$filterName] = $generator->getFilterData($attribute, $filterName);
  92. $request['aggregations'][$bucketName] = $generator->getAggregationData($attribute, $bucketName);
  93. }
  94. }
  95. /** @var $attribute Attribute */
  96. if (!$attribute->getIsSearchable() || in_array($attribute->getAttributeCode(), ['price', 'sku'], true)) {
  97. // Some fields have their own specific handlers
  98. continue;
  99. }
  100. $request = $this->processPriceAttribute($useFulltext, $attribute, $request);
  101. }
  102. return $request;
  103. }
  104. /**
  105. * Retrieve searchable attributes
  106. *
  107. * @return \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
  108. */
  109. protected function getSearchableAttributes()
  110. {
  111. /** @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection $productAttributes */
  112. $productAttributes = $this->productAttributeCollectionFactory->create();
  113. $productAttributes->addFieldToFilter(
  114. ['is_searchable', 'is_visible_in_advanced_search', 'is_filterable', 'is_filterable_in_search'],
  115. [1, 1, [1, 2], 1]
  116. );
  117. return $productAttributes;
  118. }
  119. /**
  120. * Generate advanced search request
  121. *
  122. * @return array
  123. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  124. */
  125. private function generateAdvancedSearchRequest()
  126. {
  127. $request = [];
  128. foreach ($this->getSearchableAttributes() as $attribute) {
  129. /** @var $attribute Attribute */
  130. if (!$attribute->getIsVisibleInAdvancedSearch()) {
  131. continue;
  132. }
  133. if (in_array($attribute->getAttributeCode(), ['price', 'sku'])) {
  134. //same fields have special semantics
  135. continue;
  136. }
  137. $queryName = $attribute->getAttributeCode() . '_query';
  138. $request['queries']['advanced_search_container']['queryReference'][] = [
  139. 'clause' => 'must',
  140. 'ref' => $queryName,
  141. ];
  142. switch ($attribute->getBackendType()) {
  143. case 'static':
  144. break;
  145. case 'text':
  146. case 'varchar':
  147. if ($attribute->getFrontendInput() === 'multiselect') {
  148. $filterName = $attribute->getAttributeCode() . self::FILTER_SUFFIX;
  149. $request['queries'][$queryName] = [
  150. 'name' => $queryName,
  151. 'type' => QueryInterface::TYPE_FILTER,
  152. 'filterReference' => [
  153. [
  154. 'ref' => $filterName,
  155. ],
  156. ],
  157. ];
  158. $request['filters'][$filterName] = [
  159. 'type' => FilterInterface::TYPE_TERM,
  160. 'name' => $filterName,
  161. 'field' => $attribute->getAttributeCode(),
  162. 'value' => '$' . $attribute->getAttributeCode() . '$',
  163. ];
  164. } else {
  165. $request['queries'][$queryName] = [
  166. 'name' => $queryName,
  167. 'type' => 'matchQuery',
  168. 'value' => '$' . $attribute->getAttributeCode() . '$',
  169. 'match' => [
  170. [
  171. 'field' => $attribute->getAttributeCode(),
  172. 'boost' => $attribute->getSearchWeight() ?: 1,
  173. ],
  174. ],
  175. ];
  176. }
  177. break;
  178. case 'decimal':
  179. case 'datetime':
  180. case 'date':
  181. $filterName = $attribute->getAttributeCode() . self::FILTER_SUFFIX;
  182. $request['queries'][$queryName] = [
  183. 'name' => $queryName,
  184. 'type' => QueryInterface::TYPE_FILTER,
  185. 'filterReference' => [
  186. [
  187. 'ref' => $filterName,
  188. ],
  189. ],
  190. ];
  191. $request['filters'][$filterName] = [
  192. 'field' => $attribute->getAttributeCode(),
  193. 'name' => $filterName,
  194. 'type' => FilterInterface::TYPE_RANGE,
  195. 'from' => '$' . $attribute->getAttributeCode() . '.from$',
  196. 'to' => '$' . $attribute->getAttributeCode() . '.to$',
  197. ];
  198. break;
  199. default:
  200. $filterName = $attribute->getAttributeCode() . self::FILTER_SUFFIX;
  201. $request['queries'][$queryName] = [
  202. 'name' => $queryName,
  203. 'type' => QueryInterface::TYPE_FILTER,
  204. 'filterReference' => [
  205. [
  206. 'ref' => $filterName,
  207. ],
  208. ],
  209. ];
  210. $request['filters'][$filterName] = [
  211. 'type' => FilterInterface::TYPE_TERM,
  212. 'name' => $filterName,
  213. 'field' => $attribute->getAttributeCode(),
  214. 'value' => '$' . $attribute->getAttributeCode() . '$',
  215. ];
  216. }
  217. }
  218. return $request;
  219. }
  220. /**
  221. * Modify request for price attribute.
  222. *
  223. * @param bool $useFulltext
  224. * @param Attribute $attribute
  225. * @param array $request
  226. * @return array
  227. */
  228. private function processPriceAttribute($useFulltext, $attribute, $request)
  229. {
  230. // Match search by custom price attribute isn't supported
  231. if ($useFulltext && $attribute->getFrontendInput() !== 'price') {
  232. $request['queries']['search']['match'][] = [
  233. 'field' => $attribute->getAttributeCode(),
  234. 'boost' => $attribute->getSearchWeight() ?: 1,
  235. ];
  236. }
  237. return $request;
  238. }
  239. }