PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/magento/module-catalog-search/Model/Indexer/Fulltext/Action/Full.php

https://gitlab.com/daigiangaitu91/magento
PHP | 450 lines | 205 code | 50 blank | 195 comment | 14 complexity | 7ff457a68b00631b0b484f0de9d4af8e MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\CatalogSearch\Model\Indexer\Fulltext\Action;
  7. use Magento\CatalogSearch\Model\Indexer\Fulltext;
  8. use Magento\Framework\App\ResourceConnection;
  9. /**
  10. * @SuppressWarnings(PHPMD.TooManyFields)
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. */
  13. class Full
  14. {
  15. /**
  16. * Scope identifier
  17. */
  18. const SCOPE_FIELD_NAME = 'scope';
  19. /**
  20. * Searchable attributes cache
  21. *
  22. * @var \Magento\Eav\Model\Entity\Attribute[]
  23. */
  24. protected $searchableAttributes;
  25. /**
  26. * Index values separator
  27. *
  28. * @var string
  29. */
  30. protected $separator = ' | ';
  31. /**
  32. * Array of \DateTime objects per store
  33. *
  34. * @var \DateTime[]
  35. */
  36. protected $dates = [];
  37. /**
  38. * Product Type Instances cache
  39. *
  40. * @var array
  41. */
  42. protected $productTypes = [];
  43. /**
  44. * Product Emulators cache
  45. *
  46. * @var array
  47. */
  48. protected $productEmulators = [];
  49. /**
  50. * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory
  51. */
  52. protected $productAttributeCollectionFactory;
  53. /**
  54. * Catalog product status
  55. *
  56. * @var \Magento\Catalog\Model\Product\Attribute\Source\Status
  57. */
  58. protected $catalogProductStatus;
  59. /**
  60. * Eav config
  61. *
  62. * @var \Magento\Eav\Model\Config
  63. */
  64. protected $eavConfig;
  65. /**
  66. * Catalog product type
  67. *
  68. * @var \Magento\Catalog\Model\Product\Type
  69. */
  70. protected $catalogProductType;
  71. /**
  72. * Core event manager proxy
  73. *
  74. * @var \Magento\Framework\Event\ManagerInterface
  75. */
  76. protected $eventManager;
  77. /**
  78. * Core store config
  79. *
  80. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  81. */
  82. protected $scopeConfig;
  83. /**
  84. * Store manager
  85. *
  86. * @var \Magento\Store\Model\StoreManagerInterface
  87. */
  88. protected $storeManager;
  89. /**
  90. * @var \Magento\CatalogSearch\Model\ResourceModel\Engine
  91. */
  92. protected $engine;
  93. /**
  94. * @var \Magento\Framework\Indexer\SaveHandler\IndexerInterface
  95. */
  96. protected $indexHandler;
  97. /**
  98. * @var \Magento\Framework\Stdlib\DateTime
  99. */
  100. protected $dateTime;
  101. /**
  102. * @var \Magento\Framework\Locale\ResolverInterface
  103. */
  104. protected $localeResolver;
  105. /**
  106. * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
  107. */
  108. protected $localeDate;
  109. /**
  110. * @var Resource
  111. */
  112. protected $resource;
  113. /**
  114. * @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext
  115. */
  116. protected $fulltextResource;
  117. /**
  118. * @var \Magento\Framework\Search\Request\Config
  119. */
  120. protected $searchRequestConfig;
  121. /**
  122. * @var \Magento\Framework\Search\Request\DimensionFactory
  123. */
  124. private $dimensionFactory;
  125. /**
  126. * @var \Magento\Framework\DB\Adapter\AdapterInterface
  127. */
  128. protected $connection;
  129. /**
  130. * @var \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\IndexIteratorFactory
  131. */
  132. private $iteratorFactory;
  133. /**
  134. * @param ResourceConnection $resource
  135. * @param \Magento\Catalog\Model\Product\Type $catalogProductType
  136. * @param \Magento\Eav\Model\Config $eavConfig
  137. * @param \Magento\Framework\Search\Request\Config $searchRequestConfig
  138. * @param \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus
  139. * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttributeCollectionFactory
  140. * @param \Magento\CatalogSearch\Model\ResourceModel\EngineProvider $engineProvider
  141. * @param \Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory $indexHandlerFactory
  142. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  143. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  144. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  145. * @param \Magento\Framework\Stdlib\DateTime $dateTime
  146. * @param \Magento\Framework\Locale\ResolverInterface $localeResolver
  147. * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
  148. * @param \Magento\CatalogSearch\Model\ResourceModel\Fulltext $fulltextResource
  149. * @param \Magento\Framework\Search\Request\DimensionFactory $dimensionFactory
  150. * @param \Magento\Framework\Indexer\ConfigInterface $indexerConfig
  151. * @param \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\IndexIteratorFactory $indexIteratorFactory
  152. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  153. */
  154. public function __construct(
  155. ResourceConnection $resource,
  156. \Magento\Catalog\Model\Product\Type $catalogProductType,
  157. \Magento\Eav\Model\Config $eavConfig,
  158. \Magento\Framework\Search\Request\Config $searchRequestConfig,
  159. \Magento\Catalog\Model\Product\Attribute\Source\Status $catalogProductStatus,
  160. \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $prodAttributeCollectionFactory,
  161. \Magento\CatalogSearch\Model\ResourceModel\EngineProvider $engineProvider,
  162. \Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory $indexHandlerFactory,
  163. \Magento\Framework\Event\ManagerInterface $eventManager,
  164. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  165. \Magento\Store\Model\StoreManagerInterface $storeManager,
  166. \Magento\Framework\Stdlib\DateTime $dateTime,
  167. \Magento\Framework\Locale\ResolverInterface $localeResolver,
  168. \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
  169. \Magento\CatalogSearch\Model\ResourceModel\Fulltext $fulltextResource,
  170. \Magento\Framework\Search\Request\DimensionFactory $dimensionFactory,
  171. \Magento\Framework\Indexer\ConfigInterface $indexerConfig,
  172. \Magento\CatalogSearch\Model\Indexer\Fulltext\Action\IndexIteratorFactory $indexIteratorFactory
  173. ) {
  174. $this->resource = $resource;
  175. $this->connection = $resource->getConnection();
  176. $this->catalogProductType = $catalogProductType;
  177. $this->eavConfig = $eavConfig;
  178. $this->searchRequestConfig = $searchRequestConfig;
  179. $this->catalogProductStatus = $catalogProductStatus;
  180. $this->productAttributeCollectionFactory = $prodAttributeCollectionFactory;
  181. $this->eventManager = $eventManager;
  182. $this->scopeConfig = $scopeConfig;
  183. $this->storeManager = $storeManager;
  184. $this->engine = $engineProvider->get();
  185. $configData = $indexerConfig->getIndexer(Fulltext::INDEXER_ID);
  186. $this->indexHandler = $indexHandlerFactory->create(['data' => $configData]);
  187. $this->dateTime = $dateTime;
  188. $this->localeResolver = $localeResolver;
  189. $this->localeDate = $localeDate;
  190. $this->fulltextResource = $fulltextResource;
  191. $this->dimensionFactory = $dimensionFactory;
  192. $this->iteratorFactory = $indexIteratorFactory;
  193. }
  194. /**
  195. * Rebuild whole fulltext index for all stores
  196. *
  197. * @return void
  198. */
  199. public function reindexAll()
  200. {
  201. $storeIds = array_keys($this->storeManager->getStores());
  202. foreach ($storeIds as $storeId) {
  203. $this->cleanIndex($storeId);
  204. $this->rebuildStoreIndex($storeId);
  205. }
  206. $this->searchRequestConfig->reset();
  207. }
  208. /**
  209. * Return validated table name
  210. *
  211. * @param string|string[] $table
  212. * @return string
  213. */
  214. protected function getTable($table)
  215. {
  216. return $this->resource->getTableName($table);
  217. }
  218. /**
  219. * Get parents IDs of product IDs to be re-indexed
  220. *
  221. * @param int[] $entityIds
  222. * @return int[]
  223. */
  224. protected function getProductIdsFromParents(array $entityIds)
  225. {
  226. return $this->connection
  227. ->select()
  228. ->from($this->getTable('catalog_product_relation'), 'parent_id')
  229. ->distinct(true)
  230. ->where('child_id IN (?)', $entityIds)
  231. ->where('parent_id NOT IN (?)', $entityIds)
  232. ->query()
  233. ->fetchAll(\Zend_Db::FETCH_COLUMN);
  234. }
  235. /**
  236. * Regenerate search index for specific store
  237. *
  238. * @param int $storeId Store View Id
  239. * @param int|array $productIds Product Entity Id
  240. * @return \Generator
  241. *
  242. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  243. * @SuppressWarnings(PHPMD.NPathComplexity)
  244. */
  245. public function rebuildStoreIndex($storeId, $productIds = null)
  246. {
  247. if ($productIds !== null) {
  248. $productIds = array_unique(array_merge($productIds, $this->getProductIdsFromParents($productIds)));
  249. }
  250. // prepare searchable attributes
  251. $staticFields = [];
  252. foreach ($this->getSearchableAttributes('static') as $attribute) {
  253. $staticFields[] = $attribute->getAttributeCode();
  254. }
  255. $dynamicFields = [
  256. 'int' => array_keys($this->getSearchableAttributes('int')),
  257. 'varchar' => array_keys($this->getSearchableAttributes('varchar')),
  258. 'text' => array_keys($this->getSearchableAttributes('text')),
  259. 'decimal' => array_keys($this->getSearchableAttributes('decimal')),
  260. 'datetime' => array_keys($this->getSearchableAttributes('datetime')),
  261. ];
  262. // status and visibility filter
  263. $visibility = $this->getSearchableAttribute('visibility');
  264. $status = $this->getSearchableAttribute('status');
  265. $statusIds = $this->catalogProductStatus->getVisibleStatusIds();
  266. $allowedVisibility = $this->engine->getAllowedVisibility();
  267. return $this->iteratorFactory->create([
  268. 'storeId' => $storeId,
  269. 'productIds' => $productIds,
  270. 'staticFields' => $staticFields,
  271. 'dynamicFields' => $dynamicFields,
  272. 'visibility' => $visibility,
  273. 'allowedVisibility' => $allowedVisibility,
  274. 'status' => $status,
  275. 'statusIds' => $statusIds
  276. ]);
  277. }
  278. /**
  279. * Clean search index data for store
  280. *
  281. * @param int $storeId
  282. * @return void
  283. */
  284. protected function cleanIndex($storeId)
  285. {
  286. $dimension = $this->dimensionFactory->create(['name' => self::SCOPE_FIELD_NAME, 'value' => $storeId]);
  287. $this->indexHandler->cleanIndex([$dimension]);
  288. }
  289. /**
  290. * Retrieve EAV Config Singleton
  291. *
  292. * @return \Magento\Eav\Model\Config
  293. */
  294. protected function getEavConfig()
  295. {
  296. return $this->eavConfig;
  297. }
  298. /**
  299. * Retrieve searchable attributes
  300. *
  301. * @param string $backendType
  302. * @return \Magento\Eav\Model\Entity\Attribute[]
  303. */
  304. protected function getSearchableAttributes($backendType = null)
  305. {
  306. if (null === $this->searchableAttributes) {
  307. $this->searchableAttributes = [];
  308. $productAttributes = $this->productAttributeCollectionFactory->create();
  309. $productAttributes->addToIndexFilter(true);
  310. /** @var \Magento\Eav\Model\Entity\Attribute[] $attributes */
  311. $attributes = $productAttributes->getItems();
  312. $this->eventManager->dispatch(
  313. 'catelogsearch_searchable_attributes_load_after',
  314. ['engine' => $this->engine, 'attributes' => $attributes]
  315. );
  316. $entity = $this->getEavConfig()->getEntityType(\Magento\Catalog\Model\Product::ENTITY)->getEntity();
  317. foreach ($attributes as $attribute) {
  318. $attribute->setEntity($entity);
  319. }
  320. $this->searchableAttributes = $attributes;
  321. }
  322. if ($backendType !== null) {
  323. $attributes = [];
  324. foreach ($this->searchableAttributes as $attributeId => $attribute) {
  325. if ($attribute->getBackendType() == $backendType) {
  326. $attributes[$attributeId] = $attribute;
  327. }
  328. }
  329. return $attributes;
  330. }
  331. return $this->searchableAttributes;
  332. }
  333. /**
  334. * Retrieve searchable attribute by Id or code
  335. *
  336. * @param int|string $attribute
  337. * @return \Magento\Eav\Model\Entity\Attribute
  338. */
  339. protected function getSearchableAttribute($attribute)
  340. {
  341. $attributes = $this->getSearchableAttributes();
  342. if (is_numeric($attribute)) {
  343. if (isset($attributes[$attribute])) {
  344. return $attributes[$attribute];
  345. }
  346. } elseif (is_string($attribute)) {
  347. foreach ($attributes as $attributeModel) {
  348. if ($attributeModel->getAttributeCode() == $attribute) {
  349. return $attributeModel;
  350. }
  351. }
  352. }
  353. return $this->getEavConfig()->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attribute);
  354. }
  355. /**
  356. * Returns expression for field unification
  357. *
  358. * @param string $field
  359. * @param string $backendType
  360. * @return \Zend_Db_Expr
  361. */
  362. protected function unifyField($field, $backendType = 'varchar')
  363. {
  364. if ($backendType == 'datetime') {
  365. $expr = $this->connection->getDateFormatSql($field, '%Y-%m-%d %H:%i:%s');
  366. } else {
  367. $expr = $field;
  368. }
  369. return $expr;
  370. }
  371. /**
  372. * Retrieve Product Type Instance
  373. *
  374. * @param string $typeId
  375. * @return \Magento\Catalog\Model\Product\Type\AbstractType
  376. */
  377. protected function getProductTypeInstance($typeId)
  378. {
  379. if (!isset($this->productTypes[$typeId])) {
  380. $productEmulator = $this->getProductEmulator($typeId);
  381. $this->productTypes[$typeId] = $this->catalogProductType->factory($productEmulator);
  382. }
  383. return $this->productTypes[$typeId];
  384. }
  385. /**
  386. * Retrieve Product Emulator (Magento Object)
  387. *
  388. * @param string $typeId
  389. * @return \Magento\Framework\DataObject
  390. */
  391. protected function getProductEmulator($typeId)
  392. {
  393. if (!isset($this->productEmulators[$typeId])) {
  394. $productEmulator = new \Magento\Framework\DataObject();
  395. $productEmulator->setTypeId($typeId);
  396. $this->productEmulators[$typeId] = $productEmulator;
  397. }
  398. return $this->productEmulators[$typeId];
  399. }
  400. }