Collection.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Wishlist\Model\ResourceModel\Item;
  7. use Magento\Catalog\Api\Data\ProductInterface;
  8. use Magento\Framework\EntityManager\MetadataPool;
  9. /**
  10. * Wishlist item collection
  11. * @SuppressWarnings(PHPMD.TooManyFields)
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. *
  14. * @api
  15. * @since 100.0.2
  16. */
  17. class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
  18. {
  19. /**
  20. * Product Visibility Filter to product collection flag
  21. *
  22. * @var bool
  23. */
  24. protected $_productVisible = false;
  25. /**
  26. * Product Salable Filter to product collection flag
  27. *
  28. * @var bool
  29. */
  30. protected $_productSalable = false;
  31. /**
  32. * If product out of stock, its item will be removed after load
  33. *
  34. * @var bool
  35. */
  36. protected $_productInStock = false;
  37. /**
  38. * Product Ids array
  39. *
  40. * @var array
  41. */
  42. protected $_productIds = [];
  43. /**
  44. * Store Ids array
  45. *
  46. * @var array
  47. */
  48. protected $_storeIds = [];
  49. /**
  50. * Add days in wishlist filter of product collection
  51. *
  52. * @var boolean
  53. */
  54. protected $_addDaysInWishlist = false;
  55. /**
  56. * Sum of items collection qty
  57. *
  58. * @var int
  59. */
  60. protected $_itemsQty;
  61. /**
  62. * Whether product name attribute value table is joined in select
  63. *
  64. * @var boolean
  65. */
  66. protected $_isProductNameJoined = false;
  67. /**
  68. * Adminhtml sales
  69. *
  70. * @var \Magento\Sales\Helper\Admin
  71. */
  72. protected $_adminhtmlSales = null;
  73. /**
  74. * Catalog inventory data
  75. *
  76. * @var \Magento\CatalogInventory\Api\StockConfigurationInterface
  77. */
  78. protected $stockConfiguration = null;
  79. /**
  80. * @var \Magento\Store\Model\StoreManagerInterface
  81. */
  82. protected $_storeManager;
  83. /**
  84. * @var \Magento\Framework\Stdlib\DateTime\DateTime
  85. */
  86. protected $_date;
  87. /**
  88. * @var \Magento\Wishlist\Model\Config
  89. */
  90. protected $_wishlistConfig;
  91. /**
  92. * @var \Magento\Catalog\Model\Product\Visibility
  93. */
  94. protected $_productVisibility;
  95. /**
  96. * @var \Magento\Framework\App\ResourceConnection
  97. */
  98. protected $_coreResource;
  99. /**
  100. * @var \Magento\Wishlist\Model\ResourceModel\Item\Option\CollectionFactory
  101. */
  102. protected $_optionCollectionFactory;
  103. /**
  104. * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
  105. */
  106. protected $_productCollectionFactory;
  107. /**
  108. * @var \Magento\Catalog\Model\ResourceModel\ConfigFactory
  109. */
  110. protected $_catalogConfFactory;
  111. /**
  112. * @var \Magento\Catalog\Model\Entity\AttributeFactory
  113. */
  114. protected $_catalogAttrFactory;
  115. /**
  116. * @var \Magento\Framework\App\State
  117. */
  118. protected $_appState;
  119. /**
  120. * @var MetadataPool
  121. * @since 100.1.0
  122. */
  123. protected $metadataPool;
  124. /**
  125. * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
  126. * @param \Psr\Log\LoggerInterface $logger
  127. * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
  128. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  129. * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration
  130. * @param \Magento\Sales\Helper\Admin $adminhtmlSales
  131. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  132. * @param \Magento\Framework\Stdlib\DateTime\DateTime $date
  133. * @param \Magento\Wishlist\Model\Config $wishlistConfig
  134. * @param \Magento\Catalog\Model\Product\Visibility $productVisibility
  135. * @param \Magento\Framework\App\ResourceConnection $coreResource
  136. * @param \Magento\Wishlist\Model\ResourceModel\Item\Option\CollectionFactory $optionCollectionFactory
  137. * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
  138. * @param \Magento\Catalog\Model\ResourceModel\ConfigFactory $catalogConfFactory
  139. * @param \Magento\Catalog\Model\Entity\AttributeFactory $catalogAttrFactory
  140. * @param \Magento\Wishlist\Model\ResourceModel\Item $resource
  141. * @param \Magento\Framework\App\State $appState
  142. * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
  143. *
  144. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  145. */
  146. public function __construct(
  147. \Magento\Framework\Data\Collection\EntityFactory $entityFactory,
  148. \Psr\Log\LoggerInterface $logger,
  149. \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
  150. \Magento\Framework\Event\ManagerInterface $eventManager,
  151. \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration,
  152. \Magento\Sales\Helper\Admin $adminhtmlSales,
  153. \Magento\Store\Model\StoreManagerInterface $storeManager,
  154. \Magento\Framework\Stdlib\DateTime\DateTime $date,
  155. \Magento\Wishlist\Model\Config $wishlistConfig,
  156. \Magento\Catalog\Model\Product\Visibility $productVisibility,
  157. \Magento\Framework\App\ResourceConnection $coreResource,
  158. \Magento\Wishlist\Model\ResourceModel\Item\Option\CollectionFactory $optionCollectionFactory,
  159. \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
  160. \Magento\Catalog\Model\ResourceModel\ConfigFactory $catalogConfFactory,
  161. \Magento\Catalog\Model\Entity\AttributeFactory $catalogAttrFactory,
  162. \Magento\Wishlist\Model\ResourceModel\Item $resource,
  163. \Magento\Framework\App\State $appState,
  164. \Magento\Framework\DB\Adapter\AdapterInterface $connection = null
  165. ) {
  166. $this->stockConfiguration = $stockConfiguration;
  167. $this->_adminhtmlSales = $adminhtmlSales;
  168. $this->_storeManager = $storeManager;
  169. $this->_date = $date;
  170. $this->_wishlistConfig = $wishlistConfig;
  171. $this->_productVisibility = $productVisibility;
  172. $this->_coreResource = $coreResource;
  173. $this->_optionCollectionFactory = $optionCollectionFactory;
  174. $this->_productCollectionFactory = $productCollectionFactory;
  175. $this->_catalogConfFactory = $catalogConfFactory;
  176. $this->_catalogAttrFactory = $catalogAttrFactory;
  177. $this->_appState = $appState;
  178. parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
  179. }
  180. /**
  181. * Initialize resource model for collection
  182. *
  183. * @return void
  184. */
  185. public function _construct()
  186. {
  187. $this->_init(\Magento\Wishlist\Model\Item::class, \Magento\Wishlist\Model\ResourceModel\Item::class);
  188. $this->addFilterToMap('store_id', 'main_table.store_id');
  189. }
  190. /**
  191. * Get metadata pool object
  192. *
  193. * @return MetadataPool
  194. * @since 100.1.0
  195. */
  196. protected function getMetadataPool()
  197. {
  198. if ($this->metadataPool == null) {
  199. $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
  200. ->get(\Magento\Framework\EntityManager\MetadataPool::class);
  201. }
  202. return $this->metadataPool;
  203. }
  204. /**
  205. * After load processing
  206. *
  207. * @return $this
  208. */
  209. protected function _afterLoad()
  210. {
  211. parent::_afterLoad();
  212. /**
  213. * Assign products
  214. */
  215. $this->_assignOptions();
  216. $this->_assignProducts();
  217. $this->resetItemsDataChanged();
  218. $this->getPageSize();
  219. return $this;
  220. }
  221. /**
  222. * Add options to items
  223. *
  224. * @return $this
  225. */
  226. protected function _assignOptions()
  227. {
  228. $itemIds = array_keys($this->_items);
  229. /* @var $optionCollection \Magento\Wishlist\Model\ResourceModel\Item\Option\Collection */
  230. $optionCollection = $this->_optionCollectionFactory->create();
  231. $optionCollection->addItemFilter($itemIds);
  232. /* @var $item \Magento\Wishlist\Model\Item */
  233. foreach ($this as $item) {
  234. $item->setOptions($optionCollection->getOptionsByItem($item));
  235. }
  236. $productIds = $optionCollection->getProductIds();
  237. $this->_productIds = array_merge($this->_productIds, $productIds);
  238. return $this;
  239. }
  240. /**
  241. * Add products to items and item options
  242. *
  243. * @return $this
  244. */
  245. protected function _assignProducts()
  246. {
  247. \Magento\Framework\Profiler::start(
  248. 'WISHLIST:' . __METHOD__,
  249. ['group' => 'WISHLIST', 'method' => __METHOD__]
  250. );
  251. /** @var \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection */
  252. $productCollection = $this->_productCollectionFactory->create();
  253. if ($this->_productVisible) {
  254. $productCollection->setVisibility($this->_productVisibility->getVisibleInSiteIds());
  255. }
  256. $productCollection->addPriceData()
  257. ->addTaxPercents()
  258. ->addIdFilter($this->_productIds)
  259. ->addAttributeToSelect($this->_wishlistConfig->getProductAttributes())
  260. ->addOptionsToResult()
  261. ->addUrlRewrite();
  262. if ($this->_productSalable) {
  263. $productCollection = $this->_adminhtmlSales->applySalableProductTypesFilter($productCollection);
  264. }
  265. $this->_eventManager->dispatch(
  266. 'wishlist_item_collection_products_after_load',
  267. ['product_collection' => $productCollection]
  268. );
  269. $checkInStock = $this->_productInStock && !$this->stockConfiguration->isShowOutOfStock();
  270. /** @var \Magento\Wishlist\Model\Item $item */
  271. foreach ($this as $item) {
  272. $product = $productCollection->getItemById($item->getProductId());
  273. if ($product) {
  274. if ($checkInStock && !$product->isInStock()) {
  275. $this->removeItemByKey($item->getId());
  276. } else {
  277. $product->setCustomOptions([]);
  278. $item->setProduct($product);
  279. $item->setProductName($product->getName());
  280. $item->setName($product->getName());
  281. $item->setPrice($product->getPrice());
  282. }
  283. } else {
  284. $this->removeItemByKey($item->getId());
  285. }
  286. }
  287. \Magento\Framework\Profiler::stop('WISHLIST:' . __METHOD__);
  288. return $this;
  289. }
  290. /**
  291. * Add filter by wishlist object
  292. *
  293. * @param \Magento\Wishlist\Model\Wishlist $wishlist
  294. * @return $this
  295. */
  296. public function addWishlistFilter(\Magento\Wishlist\Model\Wishlist $wishlist)
  297. {
  298. $this->addFieldToFilter('wishlist_id', $wishlist->getId());
  299. return $this;
  300. }
  301. /**
  302. * Add filtration by customer id
  303. *
  304. * @param int $customerId
  305. * @return $this
  306. */
  307. public function addCustomerIdFilter($customerId)
  308. {
  309. $this->getSelect()->join(
  310. ['wishlist' => $this->getTable('wishlist')],
  311. 'main_table.wishlist_id = wishlist.wishlist_id',
  312. []
  313. )->where(
  314. 'wishlist.customer_id = ?',
  315. $customerId
  316. );
  317. return $this;
  318. }
  319. /**
  320. * Add filter by shared stores
  321. *
  322. * @param array $storeIds
  323. * @return $this
  324. */
  325. public function addStoreFilter($storeIds = [])
  326. {
  327. if (!is_array($storeIds)) {
  328. $storeIds = [$storeIds];
  329. }
  330. $this->_storeIds = $storeIds;
  331. $this->addFieldToFilter('store_id', ['in' => $this->_storeIds]);
  332. return $this;
  333. }
  334. /**
  335. * Add items store data to collection
  336. *
  337. * @return $this
  338. */
  339. public function addStoreData()
  340. {
  341. $storeTable = $this->_coreResource->getTableName('store');
  342. $this->getSelect()->join(
  343. ['store' => $storeTable],
  344. 'main_table.store_id=store.store_id',
  345. ['store_name' => 'name', 'item_store_id' => 'store_id']
  346. );
  347. return $this;
  348. }
  349. /**
  350. * Reset sort order
  351. *
  352. * @return $this
  353. */
  354. public function resetSortOrder()
  355. {
  356. $this->getSelect()->reset(\Magento\Framework\DB\Select::ORDER);
  357. return $this;
  358. }
  359. /**
  360. * Set product Visibility Filter to product collection flag
  361. *
  362. * @param bool $flag
  363. * @return $this
  364. */
  365. public function setVisibilityFilter($flag = true)
  366. {
  367. $this->_productVisible = (bool)$flag;
  368. return $this;
  369. }
  370. /**
  371. * Set Salable Filter.
  372. *
  373. * This filter apply Salable Product Types Filter to product collection.
  374. *
  375. * @param bool $flag
  376. * @return $this
  377. */
  378. public function setSalableFilter($flag = true)
  379. {
  380. $this->_productSalable = (bool)$flag;
  381. return $this;
  382. }
  383. /**
  384. * Set In Stock Filter.
  385. *
  386. * This filter remove items with no salable product.
  387. *
  388. * @param bool $flag
  389. * @return $this
  390. */
  391. public function setInStockFilter($flag = true)
  392. {
  393. $this->_productInStock = (bool)$flag;
  394. return $this;
  395. }
  396. /**
  397. * Set flag of adding days in wishlist
  398. *
  399. * @return $this
  400. */
  401. public function addDaysInWishlist()
  402. {
  403. $this->_addDaysInWishlist = true;
  404. return $this;
  405. }
  406. /**
  407. * Adds filter on days in wishlist
  408. *
  409. * The $constraints may contain 'from' and 'to' indexes with number of days to look for items
  410. *
  411. * @param array $constraints
  412. * @return $this
  413. */
  414. public function addDaysFilter($constraints)
  415. {
  416. if (!is_array($constraints)) {
  417. return $this;
  418. }
  419. $filter = [];
  420. $gmtOffset = (new \DateTimeZone(date_default_timezone_get()))->getOffset(new \DateTime());
  421. if (isset($constraints['from'])) {
  422. $lastDay = new \DateTime();
  423. $lastDay->modify('-' . $gmtOffset . ' second')->modify('-' . $constraints['from'] . ' day');
  424. $filter['to'] = $lastDay;
  425. }
  426. if (isset($constraints['to'])) {
  427. $firstDay = new \DateTime();
  428. $firstDay->modify('-' . $gmtOffset . ' second')->modify('-' . ((int)($constraints['to']) + 1) . ' day');
  429. $filter['from'] = $firstDay;
  430. }
  431. if ($filter) {
  432. $filter['datetime'] = true;
  433. $this->addFieldToFilter('added_at', $filter);
  434. }
  435. return $this;
  436. }
  437. /**
  438. * Joins product name attribute value to use it in WHERE and ORDER clauses
  439. *
  440. * @return $this
  441. */
  442. protected function _joinProductNameTable()
  443. {
  444. if (!$this->_isProductNameJoined) {
  445. $entityTypeId = $this->_catalogConfFactory->create()->getEntityTypeId();
  446. /** @var \Magento\Catalog\Model\Entity\Attribute $attribute */
  447. $attribute = $this->_catalogAttrFactory->create()->loadByCode($entityTypeId, 'name');
  448. $storeId = $this->_storeManager->getStore(\Magento\Store\Model\Store::ADMIN_CODE)->getId();
  449. $entityMetadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
  450. $this->getSelect()->join(
  451. ['product_name_table' => $attribute->getBackendTable()],
  452. 'product_name_table.' . $entityMetadata->getLinkField() . ' = main_table.product_id' .
  453. ' AND product_name_table.store_id = ' .
  454. $storeId .
  455. ' AND product_name_table.attribute_id = ' .
  456. $attribute->getId(),
  457. []
  458. );
  459. $this->_isProductNameJoined = true;
  460. }
  461. return $this;
  462. }
  463. /**
  464. * Adds filter on product name
  465. *
  466. * @param string $productName
  467. * @return $this
  468. */
  469. public function addProductNameFilter($productName)
  470. {
  471. $this->_joinProductNameTable();
  472. $this->getSelect()->where('INSTR(product_name_table.value, ?)', $productName);
  473. return $this;
  474. }
  475. /**
  476. * Sets ordering by product name
  477. *
  478. * @param string $dir
  479. * @return $this
  480. */
  481. public function setOrderByProductName($dir)
  482. {
  483. $this->_joinProductNameTable();
  484. $this->getSelect()->order('product_name_table.value ' . $dir);
  485. return $this;
  486. }
  487. /**
  488. * Get sum of items collection qty
  489. *
  490. * @return int
  491. */
  492. public function getItemsQty()
  493. {
  494. if ($this->_itemsQty === null) {
  495. $this->_itemsQty = 0;
  496. foreach ($this as $wishlistItem) {
  497. $qty = $wishlistItem->getQty();
  498. $this->_itemsQty += $qty === 0 ? 1 : $qty;
  499. }
  500. }
  501. return (int)$this->_itemsQty;
  502. }
  503. /**
  504. * After load data
  505. *
  506. * @return $this
  507. */
  508. protected function _afterLoadData()
  509. {
  510. parent::_afterLoadData();
  511. if ($this->_addDaysInWishlist) {
  512. $gmtOffset = (int)$this->_date->getGmtOffset();
  513. $nowTimestamp = $this->_date->timestamp();
  514. foreach ($this as $wishlistItem) {
  515. $wishlistItemTimestamp = $this->_date->timestamp($wishlistItem->getAddedAt());
  516. $wishlistItem->setDaysInWishlist(
  517. (int)(($nowTimestamp - $gmtOffset - $wishlistItemTimestamp) / 24 / 60 / 60)
  518. );
  519. }
  520. }
  521. return $this;
  522. }
  523. }