Catalog.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. <?php
  2. namespace Dotdigitalgroup\Email\Model\ResourceModel;
  3. use Dotdigitalgroup\Email\Setup\Schema;
  4. /**
  5. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  6. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  7. */
  8. class Catalog extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
  9. {
  10. /**
  11. * @var \Magento\Reports\Model\ResourceModel\Product\Index\Collection\AbstractCollection
  12. */
  13. private $productIndexcollection;
  14. /**
  15. * @var \Magento\Catalog\Model\Product\Visibility
  16. */
  17. private $productVisibility;
  18. /**
  19. * @var \Magento\Catalog\Model\Config
  20. */
  21. private $config;
  22. /**
  23. * @var \Magento\Catalog\Model\ProductFactory
  24. */
  25. private $productFactory;
  26. /**
  27. * @var \Magento\Catalog\Model\CategoryFactory
  28. */
  29. private $categoryFactory;
  30. /**
  31. * @var \Magento\Reports\Model\ResourceModel\Product\CollectionFactory
  32. */
  33. private $reportProductCollection;
  34. /**
  35. * @var \Dotdigitalgroup\Email\Helper\Data
  36. */
  37. private $helper;
  38. /**
  39. * @var \Magento\Reports\Block\Product\Viewed
  40. */
  41. public $viewed;
  42. /**
  43. * @var \Magento\Reports\Model\ResourceModel\Product\Sold\CollectionFactory
  44. */
  45. public $productSoldFactory;
  46. /**
  47. * @var \Magento\Catalog\Model\ResourceModel\Category
  48. */
  49. private $categoryResource;
  50. /**
  51. * Initialize resource.
  52. *
  53. * @return null
  54. */
  55. public function _construct()
  56. {
  57. $this->_init(Schema::EMAIL_CATALOG_TABLE, 'id');
  58. }
  59. /**
  60. * Catalog constructor.
  61. *
  62. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
  63. * @param \Magento\Catalog\Model\ResourceModel\Category $categoryResource
  64. * @param \Dotdigitalgroup\Email\Helper\Data $helper
  65. * @param \Dotdigitalgroup\Email\Model\Product\Index\Collection $productIndexCollection
  66. * @param \Magento\Catalog\Model\Config $config
  67. * @param \Magento\Catalog\Model\Product\Visibility $productVisibility
  68. * @param \Magento\Catalog\Model\CategoryFactory $categoryFactory
  69. * @param \Magento\Reports\Model\ResourceModel\Product\CollectionFactory $reportProductCollection
  70. * @param \Magento\Catalog\Model\ProductFactory $productFactory
  71. * @param \Magento\Reports\Model\ResourceModel\Product\Sold\CollectionFactory $productSoldFactory
  72. * @param null $connectionName
  73. */
  74. public function __construct(
  75. \Magento\Framework\Model\ResourceModel\Db\Context $context,
  76. \Magento\Catalog\Model\ResourceModel\Category $categoryResource,
  77. \Dotdigitalgroup\Email\Helper\Data $helper,
  78. \Dotdigitalgroup\Email\Model\Product\Index\Collection $productIndexCollection,
  79. \Magento\Catalog\Model\Config $config,
  80. \Magento\Catalog\Model\Product\Visibility $productVisibility,
  81. \Magento\Catalog\Model\CategoryFactory $categoryFactory,
  82. \Magento\Reports\Model\ResourceModel\Product\CollectionFactory $reportProductCollection,
  83. \Magento\Catalog\Model\ProductFactory $productFactory,
  84. \Magento\Reports\Model\ResourceModel\Product\Sold\CollectionFactory $productSoldFactory,
  85. $connectionName = null
  86. ) {
  87. $this->helper = $helper;
  88. $this->productIndexcollection = $productIndexCollection;
  89. $this->config = $config;
  90. $this->productVisibility = $productVisibility;
  91. $this->productFactory = $productFactory;
  92. $this->categoryFactory = $categoryFactory;
  93. $this->reportProductCollection = $reportProductCollection;
  94. $this->productSoldFactory = $productSoldFactory;
  95. $this->categoryResource = $categoryResource;
  96. parent::__construct(
  97. $context,
  98. $connectionName
  99. );
  100. }
  101. /**
  102. * Get most viewed product collection.
  103. *
  104. * @param string $from
  105. * @param string $to
  106. * @param int $limit
  107. * @param int $catId
  108. * @param string $catName
  109. *
  110. * @return \Magento\Reports\Model\ResourceModel\Product\Collection
  111. */
  112. public function getMostViewedProductCollection($from, $to, $limit, $catId, $catName)
  113. {
  114. $reportProductCollection = $this->reportProductCollection->create()
  115. ->addViewsCount($from, $to)
  116. ->setPageSize($limit);
  117. //filter collection by category by category_id
  118. if ($catId) {
  119. $category = $this->categoryFactory->create();
  120. $this->categoryResource->load($category, $catId);
  121. if ($category->getId()) {
  122. $reportProductCollection->getSelect()
  123. ->joinLeft(
  124. ['ccpi' => $this->getTable('catalog_category_product_index')],
  125. 'e.entity_id = ccpi.product_id',
  126. ['category_id']
  127. )
  128. ->where('ccpi.category_id =?', $catId);
  129. } else {
  130. $this->helper->log(
  131. 'Most viewed. Category id ' . $catId
  132. . ' is invalid. It does not exist.'
  133. );
  134. }
  135. }
  136. //filter collection by category by category_name
  137. if ($catName) {
  138. $category = $this->categoryFactory->create()
  139. ->loadByAttribute('name', $catName);
  140. if ($category->getId()) {
  141. $reportProductCollection->getSelect()
  142. ->joinLeft(
  143. ['ccpi' => $this->getTable('catalog_category_product_index')],
  144. 'e.entity_id = ccpi.product_id',
  145. ['category_id']
  146. )
  147. ->where('ccpi.category_id =?', $category->getId());
  148. } else {
  149. $this->helper->log(
  150. 'Most viewed. Category name ' . $catName
  151. . ' is invalid. It does not exist.'
  152. );
  153. }
  154. }
  155. return $reportProductCollection;
  156. }
  157. /**
  158. * Get recently viewed.
  159. *
  160. * @param int $customerId
  161. * @param int $limit
  162. *
  163. * @return array
  164. */
  165. public function getRecentlyViewed($customerId, $limit)
  166. {
  167. $attributes = $this->config->getProductAttributes();
  168. $this->productIndexcollection->addAttributeToSelect($attributes);
  169. $this->productIndexcollection->setCustomerId($customerId);
  170. $this->productIndexcollection->addUrlRewrite()->setPageSize(
  171. $limit
  172. )->setCurPage(
  173. 1
  174. );
  175. /* Price data is added to consider item stock status using price index */
  176. $collection = $this->productIndexcollection->addPriceData()
  177. ->addIndexFilter()
  178. ->setAddedAtOrder()
  179. ->setVisibility($this->productVisibility->getVisibleInSiteIds());
  180. return $collection->getColumnValues('product_id');
  181. }
  182. /**
  183. * Get product collection from ids.
  184. *
  185. * @param array $ids
  186. * @param int|bool $limit
  187. *
  188. * @return array|\Magento\Catalog\Model\ResourceModel\Product\Collection
  189. */
  190. public function getProductCollectionFromIds($ids, $limit = false)
  191. {
  192. $productCollection = [];
  193. if (! empty($ids)) {
  194. $productCollection = $this->productFactory->create()
  195. ->getCollection()
  196. ->addIdFilter($ids)
  197. ->addAttributeToSelect(
  198. ['product_url', 'name', 'store_id', 'small_image', 'price']
  199. );
  200. if ($limit) {
  201. $productCollection->getSelect()->limit($limit);
  202. }
  203. }
  204. return $productCollection;
  205. }
  206. /**
  207. * Get product collection from ids.
  208. *
  209. * @param string $productsSku
  210. * @param int|bool $limit
  211. *
  212. * @return array|\Magento\Catalog\Model\ResourceModel\Product\Collection
  213. */
  214. public function getProductsCollectionBySku($productsSku, $limit = false)
  215. {
  216. $productCollection = [];
  217. if (! empty($productsSku)) {
  218. $productCollection = $this->productFactory->create()
  219. ->getCollection()
  220. ->addAttributeToSelect(
  221. ['product_url', 'name', 'store_id', 'small_image', 'price']
  222. )->addFieldToFilter('sku', ['in' => $productsSku]);
  223. if ($limit) {
  224. $productCollection->getSelect()->limit($limit);
  225. }
  226. }
  227. return $productCollection;
  228. }
  229. /**
  230. * Get bestseller collection.
  231. *
  232. * @param string $from
  233. * @param string $to
  234. * @param int $limit
  235. * @param int $storeId
  236. *
  237. * @return array|\Magento\Catalog\Model\ResourceModel\Product\Collection
  238. */
  239. public function getBestsellerCollection($from, $to, $limit, $storeId)
  240. {
  241. //create report collection
  242. $reportProductCollection = $this->productSoldFactory->create();
  243. $connection = $this->_resources->getConnection();
  244. $orderTableAliasName = $connection->quoteIdentifier('order');
  245. $fieldName = $orderTableAliasName . '.created_at';
  246. $orderTableAliasName = $connection->quoteIdentifier('order');
  247. $orderJoinCondition = [
  248. $orderTableAliasName . '.entity_id = order_items.order_id',
  249. $connection->quoteInto(
  250. "{$orderTableAliasName}.state <> ?",
  251. \Magento\Sales\Model\Order::STATE_CANCELED
  252. ),
  253. ];
  254. $orderJoinCondition[] = $this->prepareBetweenSql($fieldName, $from, $to);
  255. $reportProductCollection->getSelect()->reset()
  256. ->from(
  257. ['order_items' => $reportProductCollection->getTable('sales_order_item')],
  258. ['ordered_qty' => 'SUM(order_items.qty_ordered)', 'order_items_name' => 'order_items.name']
  259. )->joinInner(
  260. ['order' => $reportProductCollection->getTable('sales_order')],
  261. implode(' AND ', $orderJoinCondition),
  262. []
  263. )->columns(['sku'])
  264. ->where('parent_item_id IS NULL')
  265. ->group('order_items.product_id')
  266. ->having('SUM(order_items.qty_ordered) > ?', 0)
  267. ->order('ordered_qty DESC')
  268. ->limit($limit);
  269. $reportProductCollection->setStoreIds([$storeId]);
  270. $productsSku = $reportProductCollection->getColumnValues('sku');
  271. return $this->getProductsCollectionBySku($productsSku);
  272. }
  273. /**
  274. * Prepare between sql.
  275. *
  276. * @param string $fieldName Field name with table suffix ('created_at' or 'main_table.created_at')
  277. * @param string $from
  278. * @param string $to
  279. * @return string Formatted sql string
  280. */
  281. private function prepareBetweenSql($fieldName, $from, $to)
  282. {
  283. $connection = $this->_resources->getConnection();
  284. return sprintf(
  285. '(%s BETWEEN %s AND %s)',
  286. $fieldName,
  287. $connection->quote($from),
  288. $connection->quote($to)
  289. );
  290. }
  291. /**
  292. * Reset for re-import.
  293. *
  294. * @param string|null $from
  295. * @param string|null $to
  296. *
  297. * @return int
  298. *
  299. */
  300. public function resetCatalog($from = null, $to = null)
  301. {
  302. $conn = $this->getConnection();
  303. if ($from && $to) {
  304. $where = [
  305. 'created_at >= ?' => $from . ' 00:00:00',
  306. 'created_at <= ?' => $to . ' 23:59:59',
  307. 'imported is ?' => new \Zend_Db_Expr('not null')
  308. ];
  309. } else {
  310. $where = $conn->quoteInto(
  311. 'imported is ?',
  312. new \Zend_Db_Expr('not null')
  313. );
  314. }
  315. $num = $conn->update(
  316. $this->getTable(Schema::EMAIL_CATALOG_TABLE),
  317. [
  318. 'imported' => new \Zend_Db_Expr('null'),
  319. 'modified' => new \Zend_Db_Expr('null'),
  320. ],
  321. $where
  322. );
  323. return $num;
  324. }
  325. /**
  326. * Set imported in bulk query. If modified true then set modified to null in bulk query.
  327. *
  328. * @param array $ids
  329. * @param bool $modified
  330. *
  331. * @return null
  332. */
  333. public function setImportedByIds($ids, $modified = false)
  334. {
  335. try {
  336. $coreResource = $this->getConnection();
  337. $tableName = $this->getTable(Schema::EMAIL_CATALOG_TABLE);
  338. if ($modified) {
  339. $coreResource->update(
  340. $tableName,
  341. [
  342. 'modified' => new \Zend_Db_Expr('null'),
  343. 'updated_at' => gmdate('Y-m-d H:i:s'),
  344. ],
  345. ["product_id IN (?)" => $ids]
  346. );
  347. } else {
  348. $coreResource->update(
  349. $tableName,
  350. [
  351. 'imported' => '1',
  352. 'updated_at' => gmdate(
  353. 'Y-m-d H:i:s'
  354. ),
  355. ],
  356. ["product_id IN (?)" => $ids]
  357. );
  358. }
  359. } catch (\Exception $e) {
  360. $this->helper->debug((string)$e, []);
  361. }
  362. }
  363. /**
  364. * Remove product with product id set and no product
  365. *
  366. * @return null
  367. */
  368. public function removeOrphanProducts()
  369. {
  370. $write = $this->getConnection();
  371. $catalogTable = $this->getTable(Schema::EMAIL_CATALOG_TABLE);
  372. $select = $write->select();
  373. $select->reset()
  374. ->from(
  375. ['c' => $catalogTable],
  376. ['c.product_id']
  377. )
  378. ->joinLeft(
  379. [
  380. 'e' => $this->getTable(
  381. 'catalog_product_entity'
  382. ),
  383. ],
  384. 'c.product_id = e.entity_id'
  385. )
  386. ->where('e.entity_id is NULL');
  387. //delete sql statement
  388. $deleteSql = $select->deleteFromSelect('c');
  389. //run query
  390. $write->query($deleteSql);
  391. }
  392. /**
  393. * Set modified if already imported
  394. *
  395. * @param array $ids
  396. */
  397. public function setModified($ids)
  398. {
  399. $write = $this->getConnection();
  400. $tableName = $this->getTable(Schema::EMAIL_CATALOG_TABLE);
  401. $write->update(
  402. $tableName,
  403. ['modified' => 1],
  404. [
  405. $write->quoteInto("product_id IN (?)", $ids),
  406. $write->quoteInto("imported = ?", 1)
  407. ]
  408. );
  409. }
  410. }