Price.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Downloadable\Model\ResourceModel\Indexer;
  7. use Magento\Catalog\Api\Data\ProductInterface;
  8. use Magento\Catalog\Model\Product;
  9. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier;
  10. use Magento\Downloadable\Model\Product\Type;
  11. use Magento\Eav\Model\Config;
  12. use Magento\Framework\App\ResourceConnection;
  13. use Magento\Framework\Indexer\DimensionalIndexerInterface;
  14. use Magento\Framework\EntityManager\MetadataPool;
  15. use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer;
  16. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\BaseFinalPrice;
  17. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory;
  18. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
  19. /**
  20. * Downloadable Product Price Indexer Resource model
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. */
  23. class Price implements DimensionalIndexerInterface
  24. {
  25. /**
  26. * @var BaseFinalPrice
  27. */
  28. private $baseFinalPrice;
  29. /**
  30. * @var IndexTableStructureFactory
  31. */
  32. private $indexTableStructureFactory;
  33. /**
  34. * @var TableMaintainer
  35. */
  36. private $tableMaintainer;
  37. /**
  38. * @var MetadataPool
  39. */
  40. private $metadataPool;
  41. /**
  42. * @var ResourceConnection
  43. */
  44. private $resource;
  45. /**
  46. * @var string
  47. */
  48. private $connectionName;
  49. /**
  50. * @var \Magento\Framework\DB\Adapter\AdapterInterface
  51. */
  52. private $connection;
  53. /**
  54. * @var Config
  55. */
  56. private $eavConfig;
  57. /**
  58. * @var BasePriceModifier
  59. */
  60. private $basePriceModifier;
  61. /**
  62. * @param BaseFinalPrice $baseFinalPrice
  63. * @param IndexTableStructureFactory $indexTableStructureFactory
  64. * @param TableMaintainer $tableMaintainer
  65. * @param MetadataPool $metadataPool
  66. * @param Config $eavConfig
  67. * @param ResourceConnection $resource
  68. * @param BasePriceModifier $basePriceModifier
  69. * @param string $connectionName
  70. */
  71. public function __construct(
  72. BaseFinalPrice $baseFinalPrice,
  73. IndexTableStructureFactory $indexTableStructureFactory,
  74. TableMaintainer $tableMaintainer,
  75. MetadataPool $metadataPool,
  76. Config $eavConfig,
  77. ResourceConnection $resource,
  78. BasePriceModifier $basePriceModifier,
  79. $connectionName = 'indexer'
  80. ) {
  81. $this->baseFinalPrice = $baseFinalPrice;
  82. $this->indexTableStructureFactory = $indexTableStructureFactory;
  83. $this->tableMaintainer = $tableMaintainer;
  84. $this->connectionName = $connectionName;
  85. $this->metadataPool = $metadataPool;
  86. $this->resource = $resource;
  87. $this->eavConfig = $eavConfig;
  88. $this->basePriceModifier = $basePriceModifier;
  89. }
  90. /**
  91. * {@inheritdoc}
  92. * @param array $dimensions
  93. * @param \Traversable $entityIds
  94. * @throws \Exception
  95. */
  96. public function executeByDimensions(array $dimensions, \Traversable $entityIds)
  97. {
  98. $temporaryPriceTable = $this->indexTableStructureFactory->create([
  99. 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions),
  100. 'entityField' => 'entity_id',
  101. 'customerGroupField' => 'customer_group_id',
  102. 'websiteField' => 'website_id',
  103. 'taxClassField' => 'tax_class_id',
  104. 'originalPriceField' => 'price',
  105. 'finalPriceField' => 'final_price',
  106. 'minPriceField' => 'min_price',
  107. 'maxPriceField' => 'max_price',
  108. 'tierPriceField' => 'tier_price',
  109. ]);
  110. $this->fillFinalPrice($dimensions, $entityIds, $temporaryPriceTable);
  111. $this->basePriceModifier->modifyPrice($temporaryPriceTable, iterator_to_array($entityIds));
  112. $this->applyDownloadableLink($temporaryPriceTable, $dimensions);
  113. }
  114. /**
  115. * Calculate and apply Downloadable links price to index
  116. *
  117. * @param IndexTableStructure $temporaryPriceTable
  118. * @param array $dimensions
  119. * @return $this
  120. * @throws \Exception
  121. */
  122. private function applyDownloadableLink(
  123. IndexTableStructure $temporaryPriceTable,
  124. array $dimensions
  125. ) {
  126. $temporaryDownloadableTableName = 'catalog_product_index_price_downlod_temp';
  127. $this->getConnection()->createTemporaryTableLike(
  128. $temporaryDownloadableTableName,
  129. $this->getTable('catalog_product_index_price_downlod_tmp'),
  130. true
  131. );
  132. $this->fillTemporaryTable($temporaryDownloadableTableName, $dimensions);
  133. $this->updateTemporaryDownloadableTable($temporaryPriceTable->getTableName(), $temporaryDownloadableTableName);
  134. $this->getConnection()->delete($temporaryDownloadableTableName);
  135. return $this;
  136. }
  137. /**
  138. * Retrieve catalog_product attribute instance by attribute code
  139. *
  140. * @param string $attributeCode
  141. * @return \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
  142. * @throws \Magento\Framework\Exception\LocalizedException
  143. */
  144. protected function getAttribute($attributeCode)
  145. {
  146. return $this->eavConfig->getAttribute(Product::ENTITY, $attributeCode);
  147. }
  148. /**
  149. * Put data into catalog product price indexer Downloadable links price temp table
  150. *
  151. * @param string $temporaryDownloadableTableName
  152. * @param array $dimensions
  153. * @return void
  154. * @throws \Exception
  155. */
  156. private function fillTemporaryTable(string $temporaryDownloadableTableName, array $dimensions)
  157. {
  158. $dlType = $this->getAttribute('links_purchased_separately');
  159. $ifPrice = $this->getConnection()->getIfNullSql('dlpw.price_id', 'dlpd.price');
  160. $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
  161. $linkField = $metadata->getLinkField();
  162. $select = $this->getConnection()->select()->from(
  163. ['i' => $this->tableMaintainer->getMainTmpTable($dimensions)],
  164. ['entity_id', 'customer_group_id', 'website_id']
  165. )->join(
  166. ['dl' => $dlType->getBackend()->getTable()],
  167. "dl.{$linkField} = i.entity_id AND dl.attribute_id = {$dlType->getAttributeId()}" . " AND dl.store_id = 0",
  168. []
  169. )->join(
  170. ['dll' => $this->getTable('downloadable_link')],
  171. 'dll.product_id = i.entity_id',
  172. []
  173. )->join(
  174. ['dlpd' => $this->getTable('downloadable_link_price')],
  175. 'dll.link_id = dlpd.link_id AND dlpd.website_id = 0',
  176. []
  177. )->joinLeft(
  178. ['dlpw' => $this->getTable('downloadable_link_price')],
  179. 'dlpd.link_id = dlpw.link_id AND dlpw.website_id = i.website_id',
  180. []
  181. )->where(
  182. 'dl.value = ?',
  183. 1
  184. )->group(
  185. ['i.entity_id', 'i.customer_group_id', 'i.website_id']
  186. )->columns(
  187. [
  188. 'min_price' => new \Zend_Db_Expr('MIN(' . $ifPrice . ')'),
  189. 'max_price' => new \Zend_Db_Expr('SUM(' . $ifPrice . ')'),
  190. ]
  191. );
  192. $query = $select->insertFromSelect($temporaryDownloadableTableName);
  193. $this->getConnection()->query($query);
  194. }
  195. /**
  196. * Update data in the catalog product price indexer temp table
  197. *
  198. * @param string $temporaryPriceTableName
  199. * @param string $temporaryDownloadableTableName
  200. * @return void
  201. */
  202. private function updateTemporaryDownloadableTable(
  203. string $temporaryPriceTableName,
  204. string $temporaryDownloadableTableName
  205. ) {
  206. $ifTierPrice = $this->getConnection()->getCheckSql(
  207. 'i.tier_price IS NOT NULL',
  208. '(i.tier_price + id.min_price)',
  209. 'NULL'
  210. );
  211. $selectForCrossUpdate = $this->getConnection()->select()->join(
  212. ['id' => $temporaryDownloadableTableName],
  213. 'i.entity_id = id.entity_id AND i.customer_group_id = id.customer_group_id' .
  214. ' AND i.website_id = id.website_id',
  215. []
  216. );
  217. // adds price of custom option, that was applied in DefaultPrice::_applyCustomOption
  218. $selectForCrossUpdate->columns(
  219. [
  220. 'min_price' => new \Zend_Db_Expr('i.min_price + id.min_price'),
  221. 'max_price' => new \Zend_Db_Expr('i.max_price + id.max_price'),
  222. 'tier_price' => new \Zend_Db_Expr($ifTierPrice),
  223. ]
  224. );
  225. $query = $selectForCrossUpdate->crossUpdateFromSelect(['i' => $temporaryPriceTableName]);
  226. $this->getConnection()->query($query);
  227. }
  228. /**
  229. * Fill final price
  230. *
  231. * @param array $dimensions
  232. * @param \Traversable $entityIds
  233. * @param IndexTableStructure $temporaryPriceTable
  234. * @throws \Magento\Framework\Exception\LocalizedException
  235. * @throws \Zend_Db_Select_Exception
  236. */
  237. private function fillFinalPrice(
  238. array $dimensions,
  239. \Traversable $entityIds,
  240. IndexTableStructure $temporaryPriceTable
  241. ) {
  242. $select = $this->baseFinalPrice->getQuery($dimensions, Type::TYPE_DOWNLOADABLE, iterator_to_array($entityIds));
  243. $query = $select->insertFromSelect($temporaryPriceTable->getTableName(), [], false);
  244. $this->tableMaintainer->getConnection()->query($query);
  245. }
  246. /**
  247. * Get connection
  248. *
  249. * return \Magento\Framework\DB\Adapter\AdapterInterface
  250. * @throws \DomainException
  251. */
  252. private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface
  253. {
  254. if ($this->connection === null) {
  255. $this->connection = $this->resource->getConnection($this->connectionName);
  256. }
  257. return $this->connection;
  258. }
  259. /**
  260. * Get table
  261. *
  262. * @param string $tableName
  263. * @return string
  264. */
  265. private function getTable($tableName)
  266. {
  267. return $this->resource->getTableName($tableName, $this->connectionName);
  268. }
  269. }