PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/core/Mage/Catalog/Model/Product.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 1843 lines | 995 code | 180 blank | 668 comment | 83 complexity | 3277f92c61186a3ac36e7b69c3d509f0 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Catalog
  23. * @copyright Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Catalog product model
  28. *
  29. * @category Mage
  30. * @package Mage_Catalog
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract
  34. {
  35. /**
  36. * Entity code.
  37. * Can be used as part of method name for entity processing
  38. */
  39. const ENTITY = 'catalog_product';
  40. const CACHE_TAG = 'catalog_product';
  41. protected $_cacheTag = 'catalog_product';
  42. protected $_eventPrefix = 'catalog_product';
  43. protected $_eventObject = 'product';
  44. protected $_canAffectOptions = false;
  45. /**
  46. * Product type instance
  47. *
  48. * @var Mage_Catalog_Model_Product_Type_Abstract
  49. */
  50. protected $_typeInstance = null;
  51. /**
  52. * Product type instance as singleton
  53. */
  54. protected $_typeInstanceSingleton = null;
  55. /**
  56. * Product link instance
  57. *
  58. * @var Mage_Catalog_Model_Product_Link
  59. */
  60. protected $_linkInstance;
  61. /**
  62. * Product object customization (not stored in DB)
  63. *
  64. * @var array
  65. */
  66. protected $_customOptions = array();
  67. /**
  68. * Product Url Instance
  69. *
  70. * @var Mage_Catalog_Model_Product_Url
  71. */
  72. protected $_urlModel = null;
  73. protected static $_url;
  74. protected static $_urlRewrite;
  75. protected $_errors = array();
  76. protected $_optionInstance;
  77. protected $_options = array();
  78. /**
  79. * Product reserved attribute codes
  80. */
  81. protected $_reservedAttributes;
  82. /**
  83. * Flag for available duplicate function
  84. *
  85. * @var boolean
  86. */
  87. protected $_isDuplicable = true;
  88. /**
  89. * Initialize resources
  90. */
  91. protected function _construct()
  92. {
  93. $this->_init('catalog/product');
  94. }
  95. /**
  96. * Retrieve Store Id
  97. *
  98. * @return int
  99. */
  100. public function getStoreId()
  101. {
  102. if ($this->hasData('store_id')) {
  103. return $this->getData('store_id');
  104. }
  105. return Mage::app()->getStore()->getId();
  106. }
  107. /**
  108. * Get collection instance
  109. *
  110. * @return object
  111. */
  112. public function getResourceCollection()
  113. {
  114. if (empty($this->_resourceCollectionName)) {
  115. Mage::throwException(Mage::helper('core')->__('The model collection resource name is not defined.'));
  116. }
  117. $collection = Mage::getResourceModel($this->_resourceCollectionName);
  118. $collection->setStoreId($this->getStoreId());
  119. return $collection;
  120. }
  121. /**
  122. * Get product url model
  123. *
  124. * @return Mage_Catalog_Model_Product_Url
  125. */
  126. public function getUrlModel()
  127. {
  128. if ($this->_urlModel === null) {
  129. $this->_urlModel = Mage::getSingleton('catalog/product_url');
  130. }
  131. return $this->_urlModel;
  132. }
  133. /**
  134. * Validate Product Data
  135. *
  136. * @todo implement full validation process with errors returning which are ignoring now
  137. *
  138. * @return Mage_Catalog_Model_Product
  139. */
  140. public function validate()
  141. {
  142. // $this->getAttributes();
  143. // Mage::dispatchEvent($this->_eventPrefix.'_validate_before', array($this->_eventObject=>$this));
  144. // $result = $this->_getResource()->validate($this);
  145. // Mage::dispatchEvent($this->_eventPrefix.'_validate_after', array($this->_eventObject=>$this));
  146. // return $result;
  147. Mage::dispatchEvent($this->_eventPrefix.'_validate_before', array($this->_eventObject=>$this));
  148. $this->_getResource()->validate($this);
  149. Mage::dispatchEvent($this->_eventPrefix.'_validate_after', array($this->_eventObject=>$this));
  150. return $this;
  151. }
  152. /**
  153. * Get product name
  154. *
  155. * @return string
  156. */
  157. public function getName()
  158. {
  159. return $this->_getData('name');
  160. }
  161. /**
  162. * Get product price throught type instance
  163. *
  164. * @return unknown
  165. */
  166. public function getPrice()
  167. {
  168. return $this->getPriceModel()->getPrice($this);
  169. }
  170. /**
  171. * Get product type identifier
  172. *
  173. * @return string
  174. */
  175. public function getTypeId()
  176. {
  177. return $this->_getData('type_id');
  178. }
  179. /**
  180. * Get product status
  181. *
  182. * @return int
  183. */
  184. public function getStatus()
  185. {
  186. return $this->_getData('status');
  187. }
  188. /**
  189. * Retrieve type instance
  190. *
  191. * Type instance implement type depended logic
  192. *
  193. * @param bool $singleton
  194. * @return Mage_Catalog_Model_Product_Type_Abstract
  195. */
  196. public function getTypeInstance($singleton = false)
  197. {
  198. if ($singleton === true) {
  199. if (is_null($this->_typeInstanceSingleton)) {
  200. $this->_typeInstanceSingleton = Mage::getSingleton('catalog/product_type')
  201. ->factory($this, true);
  202. }
  203. return $this->_typeInstanceSingleton;
  204. }
  205. if ($this->_typeInstance === null) {
  206. $this->_typeInstance = Mage::getSingleton('catalog/product_type')
  207. ->factory($this);
  208. }
  209. return $this->_typeInstance;
  210. }
  211. /**
  212. * Set type instance for external
  213. *
  214. * @param Mage_Catalog_Model_Product_Type_Abstract $singleton
  215. * @param bool $singleton
  216. * @return Mage_Catalog_Model_Product
  217. */
  218. public function setTypeInstance($instance, $singleton = false)
  219. {
  220. if ($singleton === true) {
  221. $this->_typeInstanceSingleton = $instance;
  222. } else {
  223. $this->_typeInstance = $instance;
  224. }
  225. return $this;
  226. }
  227. /**
  228. * Retrieve link instance
  229. *
  230. * @return Mage_Catalog_Model_Product_Link
  231. */
  232. public function getLinkInstance()
  233. {
  234. if (!$this->_linkInstance) {
  235. $this->_linkInstance = Mage::getSingleton('catalog/product_link');
  236. }
  237. return $this->_linkInstance;
  238. }
  239. /**
  240. * Retrive product id by sku
  241. *
  242. * @param string $sku
  243. * @return integer
  244. */
  245. public function getIdBySku($sku)
  246. {
  247. return $this->_getResource()->getIdBySku($sku);
  248. }
  249. /**
  250. * Retrieve product category id
  251. *
  252. * @return int
  253. */
  254. public function getCategoryId()
  255. {
  256. if ($category = Mage::registry('current_category')) {
  257. return $category->getId();
  258. }
  259. return false;
  260. }
  261. /**
  262. * Retrieve product category
  263. *
  264. * @return Mage_Catalog_Model_Category
  265. */
  266. public function getCategory()
  267. {
  268. $category = $this->getData('category');
  269. if (is_null($category) && $this->getCategoryId()) {
  270. $category = Mage::getModel('catalog/category')->load($this->getCategoryId());
  271. $this->setCategory($category);
  272. }
  273. return $category;
  274. }
  275. /**
  276. * Set assigned category IDs array to product
  277. *
  278. * @param array|string $ids
  279. * @return Mage_Catalog_Model_Product
  280. */
  281. public function setCategoryIds($ids)
  282. {
  283. if (is_string($ids)) {
  284. $ids = explode(',', $ids);
  285. } elseif (!is_array($ids)) {
  286. Mage::throwException(Mage::helper('catalog')->__('Invalid category IDs.'));
  287. }
  288. foreach ($ids as $i => $v) {
  289. if (empty($v)) {
  290. unset($ids[$i]);
  291. }
  292. }
  293. $this->setData('category_ids', $ids);
  294. return $this;
  295. }
  296. /**
  297. * Retrieve assigned category Ids
  298. *
  299. * @return array
  300. */
  301. public function getCategoryIds()
  302. {
  303. if (! $this->hasData('category_ids')) {
  304. $wasLocked = false;
  305. if ($this->isLockedAttribute('category_ids')) {
  306. $wasLocked = true;
  307. $this->unlockAttribute('category_ids');
  308. }
  309. $ids = $this->_getResource()->getCategoryIds($this);
  310. $this->setData('category_ids', $ids);
  311. if ($wasLocked) {
  312. $this->lockAttribute('category_ids');
  313. }
  314. }
  315. return (array) $this->_getData('category_ids');
  316. }
  317. /**
  318. * Retrieve product categories
  319. *
  320. * @return Varien_Data_Collection
  321. */
  322. public function getCategoryCollection()
  323. {
  324. return $this->_getResource()->getCategoryCollection($this);
  325. }
  326. /**
  327. * Retrieve product websites identifiers
  328. *
  329. * @return array
  330. */
  331. public function getWebsiteIds()
  332. {
  333. if (!$this->hasWebsiteIds()) {
  334. $ids = $this->_getResource()->getWebsiteIds($this);
  335. $this->setWebsiteIds($ids);
  336. }
  337. return $this->getData('website_ids');
  338. }
  339. /**
  340. * Get all sore ids where product is presented
  341. *
  342. * @return array
  343. */
  344. public function getStoreIds()
  345. {
  346. if (!$this->hasStoreIds()) {
  347. $storeIds = array();
  348. if ($websiteIds = $this->getWebsiteIds()) {
  349. foreach ($websiteIds as $websiteId) {
  350. $websiteStores = Mage::app()->getWebsite($websiteId)->getStoreIds();
  351. $storeIds = array_merge($storeIds, $websiteStores);
  352. }
  353. }
  354. $this->setStoreIds($storeIds);
  355. }
  356. return $this->getData('store_ids');
  357. }
  358. /**
  359. * Retrieve product attributes
  360. *
  361. * if $groupId is null - retrieve all product attributes
  362. *
  363. * @param int $groupId
  364. * @return array
  365. */
  366. public function getAttributes($groupId = null, $skipSuper=false)
  367. {
  368. $productAttributes = $this->getTypeInstance(true)->getEditableAttributes($this);
  369. if ($groupId) {
  370. $attributes = array();
  371. foreach ($productAttributes as $attribute) {
  372. if ($attribute->isInGroup($this->getAttributeSetId(), $groupId)) {
  373. $attributes[] = $attribute;
  374. }
  375. }
  376. } else {
  377. $attributes = $productAttributes;
  378. }
  379. return $attributes;
  380. }
  381. /**
  382. * Check product options and type options and save them, too
  383. */
  384. protected function _beforeSave()
  385. {
  386. $this->cleanCache();
  387. $this->setTypeHasOptions(false);
  388. $this->setTypeHasRequiredOptions(false);
  389. $this->getTypeInstance(true)->beforeSave($this);
  390. $hasOptions = false;
  391. $hasRequiredOptions = false;
  392. /**
  393. * $this->_canAffectOptions - set by type instance only
  394. * $this->getCanSaveCustomOptions() - set either in controller when "Custom Options" ajax tab is loaded,
  395. * or in type instance as well
  396. */
  397. $this->canAffectOptions($this->_canAffectOptions && $this->getCanSaveCustomOptions());
  398. if ($this->getCanSaveCustomOptions()) {
  399. $options = $this->getProductOptions();
  400. if (is_array($options)) {
  401. $this->setIsCustomOptionChanged(true);
  402. foreach ($this->getProductOptions() as $option) {
  403. $this->getOptionInstance()->addOption($option);
  404. if ((!isset($option['is_delete'])) || $option['is_delete'] != '1') {
  405. $hasOptions = true;
  406. }
  407. }
  408. foreach ($this->getOptionInstance()->getOptions() as $option) {
  409. if ($option['is_require'] == '1') {
  410. $hasRequiredOptions = true;
  411. break;
  412. }
  413. }
  414. }
  415. }
  416. /**
  417. * Set true, if any
  418. * Set false, ONLY if options have been affected by Options tab and Type instance tab
  419. */
  420. if ($hasOptions || (bool)$this->getTypeHasOptions()) {
  421. $this->setHasOptions(true);
  422. if ($hasRequiredOptions || (bool)$this->getTypeHasRequiredOptions()) {
  423. $this->setRequiredOptions(true);
  424. } elseif ($this->canAffectOptions()) {
  425. $this->setRequiredOptions(false);
  426. }
  427. } elseif ($this->canAffectOptions()) {
  428. $this->setHasOptions(false);
  429. $this->setRequiredOptions(false);
  430. }
  431. parent::_beforeSave();
  432. }
  433. /**
  434. * Check/set if options can be affected when saving product
  435. * If value specified, it will be set.
  436. *
  437. * @param bool $value
  438. * @return bool
  439. */
  440. public function canAffectOptions($value = null)
  441. {
  442. if (null !== $value) {
  443. $this->_canAffectOptions = (bool)$value;
  444. }
  445. return $this->_canAffectOptions;
  446. }
  447. /**
  448. * Saving product type related data
  449. *
  450. * @return Mage_Catalog_Model_Product
  451. */
  452. protected function _afterSave()
  453. {
  454. $this->getLinkInstance()->saveProductRelations($this);
  455. $this->getTypeInstance(true)->save($this);
  456. /**
  457. * Product Options
  458. */
  459. $this->getOptionInstance()->setProduct($this)
  460. ->saveOptions();
  461. return parent::_afterSave();
  462. }
  463. /**
  464. * Init indexing process after product data commit
  465. *
  466. * @return Mage_Catalog_Model_Product
  467. */
  468. public function afterCommitCallback()
  469. {
  470. parent::afterCommitCallback();
  471. Mage::getSingleton('index/indexer')->processEntityAction(
  472. $this, self::ENTITY, Mage_Index_Model_Event::TYPE_SAVE
  473. );
  474. return $this;
  475. }
  476. /**
  477. * Clear chache related with product and protect delete from not admin
  478. * Register indexing event before delete product
  479. *
  480. * @return Mage_Catalog_Model_Product
  481. */
  482. protected function _beforeDelete()
  483. {
  484. $this->_protectFromNonAdmin();
  485. $this->cleanCache();
  486. Mage::getSingleton('index/indexer')->logEvent(
  487. $this, self::ENTITY, Mage_Index_Model_Event::TYPE_DELETE
  488. );
  489. return parent::_beforeDelete();
  490. }
  491. /**
  492. * Init indexing process after product delete commit
  493. *
  494. * @return Mage_Catalog_Model_Product
  495. */
  496. protected function _afterDeleteCommit()
  497. {
  498. parent::_afterDeleteCommit();
  499. Mage::getSingleton('index/indexer')->indexEvents(
  500. self::ENTITY, Mage_Index_Model_Event::TYPE_DELETE
  501. );
  502. }
  503. /**
  504. * Load product options if they exists
  505. *
  506. * @return Mage_Catalog_Model_Product
  507. */
  508. protected function _afterLoad()
  509. {
  510. parent::_afterLoad();
  511. /**
  512. * Load product options
  513. */
  514. if ($this->getHasOptions()) {
  515. foreach ($this->getProductOptionsCollection() as $option) {
  516. $option->setProduct($this);
  517. $this->addOption($option);
  518. }
  519. }
  520. return $this;
  521. }
  522. /**
  523. * Retrieve resource instance wrapper
  524. *
  525. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product
  526. */
  527. protected function _getResource()
  528. {
  529. return parent::_getResource();
  530. }
  531. /**
  532. * Clear cache related with product id
  533. *
  534. * @return Mage_Catalog_Model_Product
  535. */
  536. public function cleanCache()
  537. {
  538. Mage::app()->cleanCache('catalog_product_'.$this->getId());
  539. return $this;
  540. }
  541. /**
  542. * Get product price model
  543. *
  544. * @return Mage_Catalog_Model_Product_Type_Price
  545. */
  546. public function getPriceModel()
  547. {
  548. return Mage::getSingleton('catalog/product_type')->priceFactory($this->getTypeId());
  549. }
  550. /**
  551. * Get product tier price by qty
  552. *
  553. * @param double $qty
  554. * @return double
  555. */
  556. public function getTierPrice($qty=null)
  557. {
  558. return $this->getPriceModel()->getTierPrice($qty, $this);
  559. }
  560. /**
  561. * Count how many tier prices we have for the product
  562. *
  563. * @return int
  564. */
  565. public function getTierPriceCount()
  566. {
  567. return $this->getPriceModel()->getTierPriceCount($this);
  568. }
  569. /**
  570. * Get formated by currency tier price
  571. *
  572. * @param double $qty
  573. * @return array || double
  574. */
  575. public function getFormatedTierPrice($qty=null)
  576. {
  577. return $this->getPriceModel()->getFormatedTierPrice($qty, $this);
  578. }
  579. /**
  580. * Get formated by currency product price
  581. *
  582. * @return array || double
  583. */
  584. public function getFormatedPrice()
  585. {
  586. return $this->getPriceModel()->getFormatedPrice($this);
  587. }
  588. /**
  589. * Sets final price of product
  590. *
  591. * This func is equal to magic 'setFinalPrice()', but added as a separate func, because in cart with bundle
  592. * products it's called very often in Item->getProduct(). So removing chain of magic with more cpu consuming
  593. * algorithms gives nice optimization boost.
  594. *
  595. * @return Mage_Catalog_Model_Product
  596. */
  597. public function setFinalPrice($price)
  598. {
  599. $this->_data['final_price'] = $price;
  600. return $this;
  601. }
  602. /**
  603. * Get product final price
  604. *
  605. * @param double $qty
  606. * @return double
  607. */
  608. public function getFinalPrice($qty=null)
  609. {
  610. $price = $this->_getData('final_price');
  611. if ($price !== null) {
  612. return $price;
  613. }
  614. return $this->getPriceModel()->getFinalPrice($qty, $this);
  615. }
  616. public function getCalculatedFinalPrice()
  617. {
  618. return $this->_getData('calculated_final_price');
  619. }
  620. public function getMinimalPrice()
  621. {
  622. return max($this->_getData('minimal_price'), 0);
  623. }
  624. public function getSpecialPrice()
  625. {
  626. return $this->_getData('special_price');
  627. }
  628. public function getSpecialFromDate()
  629. {
  630. return $this->_getData('special_from_date');
  631. }
  632. public function getSpecialToDate()
  633. {
  634. return $this->_getData('special_to_date');
  635. }
  636. /*******************************************************************************
  637. ** Linked products API
  638. */
  639. /**
  640. * Retrieve array of related roducts
  641. *
  642. * @return array
  643. */
  644. public function getRelatedProducts()
  645. {
  646. if (!$this->hasRelatedProducts()) {
  647. $products = array();
  648. $collection = $this->getRelatedProductCollection();
  649. foreach ($collection as $product) {
  650. $products[] = $product;
  651. }
  652. $this->setRelatedProducts($products);
  653. }
  654. return $this->getData('related_products');
  655. }
  656. /**
  657. * Retrieve related products identifiers
  658. *
  659. * @return array
  660. */
  661. public function getRelatedProductIds()
  662. {
  663. if (!$this->hasRelatedProductIds()) {
  664. $ids = array();
  665. foreach ($this->getRelatedProducts() as $product) {
  666. $ids[] = $product->getId();
  667. }
  668. $this->setRelatedProductIds($ids);
  669. }
  670. return $this->getData('related_product_ids');
  671. }
  672. /**
  673. * Retrieve collection related product
  674. */
  675. public function getRelatedProductCollection()
  676. {
  677. $collection = $this->getLinkInstance()->useRelatedLinks()
  678. ->getProductCollection()
  679. ->setIsStrongMode();
  680. $collection->setProduct($this);
  681. return $collection;
  682. }
  683. /**
  684. * Retrieve collection related link
  685. */
  686. public function getRelatedLinkCollection()
  687. {
  688. $collection = $this->getLinkInstance()->useRelatedLinks()
  689. ->getLinkCollection();
  690. $collection->setProduct($this);
  691. $collection->addLinkTypeIdFilter();
  692. $collection->addProductIdFilter();
  693. $collection->joinAttributes();
  694. return $collection;
  695. }
  696. /**
  697. * Retrieve array of up sell products
  698. *
  699. * @return array
  700. */
  701. public function getUpSellProducts()
  702. {
  703. if (!$this->hasUpSellProducts()) {
  704. $products = array();
  705. foreach ($this->getUpSellProductCollection() as $product) {
  706. $products[] = $product;
  707. }
  708. $this->setUpSellProducts($products);
  709. }
  710. return $this->getData('up_sell_products');
  711. }
  712. /**
  713. * Retrieve up sell products identifiers
  714. *
  715. * @return array
  716. */
  717. public function getUpSellProductIds()
  718. {
  719. if (!$this->hasUpSellProductIds()) {
  720. $ids = array();
  721. foreach ($this->getUpSellProducts() as $product) {
  722. $ids[] = $product->getId();
  723. }
  724. $this->setUpSellProductIds($ids);
  725. }
  726. return $this->getData('up_sell_product_ids');
  727. }
  728. /**
  729. * Retrieve collection up sell product
  730. */
  731. public function getUpSellProductCollection()
  732. {
  733. $collection = $this->getLinkInstance()->useUpSellLinks()
  734. ->getProductCollection()
  735. ->setIsStrongMode();
  736. $collection->setProduct($this);
  737. return $collection;
  738. }
  739. /**
  740. * Retrieve collection up sell link
  741. */
  742. public function getUpSellLinkCollection()
  743. {
  744. $collection = $this->getLinkInstance()->useUpSellLinks()
  745. ->getLinkCollection();
  746. $collection->setProduct($this);
  747. $collection->addLinkTypeIdFilter();
  748. $collection->addProductIdFilter();
  749. $collection->joinAttributes();
  750. return $collection;
  751. }
  752. /**
  753. * Retrieve array of cross sell products
  754. *
  755. * @return array
  756. */
  757. public function getCrossSellProducts()
  758. {
  759. if (!$this->hasCrossSellProducts()) {
  760. $products = array();
  761. foreach ($this->getCrossSellProductCollection() as $product) {
  762. $products[] = $product;
  763. }
  764. $this->setCrossSellProducts($products);
  765. }
  766. return $this->getData('cross_sell_products');
  767. }
  768. /**
  769. * Retrieve cross sell products identifiers
  770. *
  771. * @return array
  772. */
  773. public function getCrossSellProductIds()
  774. {
  775. if (!$this->hasCrossSellProductIds()) {
  776. $ids = array();
  777. foreach ($this->getCrossSellProducts() as $product) {
  778. $ids[] = $product->getId();
  779. }
  780. $this->setCrossSellProductIds($ids);
  781. }
  782. return $this->getData('cross_sell_product_ids');
  783. }
  784. /**
  785. * Retrieve collection cross sell product
  786. *
  787. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection
  788. */
  789. public function getCrossSellProductCollection()
  790. {
  791. $collection = $this->getLinkInstance()->useCrossSellLinks()
  792. ->getProductCollection()
  793. ->setIsStrongMode();
  794. $collection->setProduct($this);
  795. return $collection;
  796. }
  797. /**
  798. * Retrieve collection cross sell link
  799. */
  800. public function getCrossSellLinkCollection()
  801. {
  802. $collection = $this->getLinkInstance()->useCrossSellLinks()
  803. ->getLinkCollection();
  804. $collection->setProduct($this);
  805. $collection->addLinkTypeIdFilter();
  806. $collection->addProductIdFilter();
  807. $collection->joinAttributes();
  808. return $collection;
  809. }
  810. /**
  811. * Retrieve collection grouped link
  812. */
  813. public function getGroupedLinkCollection()
  814. {
  815. $collection = $this->getLinkInstance()->useGroupedLinks()
  816. ->getLinkCollection();
  817. $collection->setProduct($this);
  818. $collection->addLinkTypeIdFilter();
  819. $collection->addProductIdFilter();
  820. $collection->joinAttributes();
  821. return $collection;
  822. }
  823. /*******************************************************************************
  824. ** Media API
  825. */
  826. /**
  827. * Retrive attributes for media gallery
  828. *
  829. * @return array
  830. */
  831. public function getMediaAttributes()
  832. {
  833. if (!$this->hasMediaAttributes()) {
  834. $mediaAttributes = array();
  835. foreach ($this->getAttributes() as $attribute) {
  836. if($attribute->getFrontend()->getInputType() == 'media_image') {
  837. $mediaAttributes[$attribute->getAttributeCode()] = $attribute;
  838. }
  839. }
  840. $this->setMediaAttributes($mediaAttributes);
  841. }
  842. return $this->getData('media_attributes');
  843. }
  844. /**
  845. * Retrive media gallery images
  846. *
  847. * @return Varien_Data_Collection
  848. */
  849. public function getMediaGalleryImages()
  850. {
  851. if(!$this->hasData('media_gallery_images') && is_array($this->getMediaGallery('images'))) {
  852. $images = new Varien_Data_Collection();
  853. foreach ($this->getMediaGallery('images') as $image) {
  854. if ($image['disabled']) {
  855. continue;
  856. }
  857. $image['url'] = $this->getMediaConfig()->getMediaUrl($image['file']);
  858. $image['id'] = isset($image['value_id']) ? $image['value_id'] : null;
  859. $image['path'] = $this->getMediaConfig()->getMediaPath($image['file']);
  860. $images->addItem(new Varien_Object($image));
  861. }
  862. $this->setData('media_gallery_images', $images);
  863. }
  864. return $this->getData('media_gallery_images');
  865. }
  866. /**
  867. * Add image to media gallery
  868. *
  869. * @param string $file file path of image in file system
  870. * @param string|array $mediaAttribute code of attribute with type 'media_image',
  871. * leave blank if image should be only in gallery
  872. * @param boolean $move if true, it will move source file
  873. * @param boolean $exclude mark image as disabled in product page view
  874. */
  875. public function addImageToMediaGallery($file, $mediaAttribute=null, $move=false, $exclude=true)
  876. {
  877. $attributes = $this->getTypeInstance(true)->getSetAttributes($this);
  878. if (!isset($attributes['media_gallery'])) {
  879. return $this;
  880. }
  881. $mediaGalleryAttribute = $attributes['media_gallery'];
  882. /* @var $mediaGalleryAttribute Mage_Catalog_Model_Resource_Eav_Attribute */
  883. $mediaGalleryAttribute->getBackend()->addImage($this, $file, $mediaAttribute, $move, $exclude);
  884. return $this;
  885. }
  886. /**
  887. * Retrive product media config
  888. *
  889. * @return Mage_Catalog_Model_Product_Media_Config
  890. */
  891. public function getMediaConfig()
  892. {
  893. return Mage::getSingleton('catalog/product_media_config');
  894. }
  895. /**
  896. * Create duplicate
  897. *
  898. * @return Mage_Catalog_Model_Product
  899. */
  900. public function duplicate()
  901. {
  902. $this->getWebsiteIds();
  903. $this->getCategoryIds();
  904. $newProduct = Mage::getModel('catalog/product')->setData($this->getData())
  905. ->setIsDuplicate(true)
  906. ->setOriginalId($this->getId())
  907. ->setSku(null)
  908. ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED)
  909. ->setCreatedAt(null)
  910. ->setUpdatedAt(null)
  911. ->setId(null)
  912. ->setStoreId(Mage::app()->getStore()->getId());
  913. Mage::dispatchEvent(
  914. 'catalog_model_product_duplicate',
  915. array('current_product'=>$this, 'new_product'=>$newProduct)
  916. );
  917. /* @var $newProduct Mage_Catalog_Model_Product */
  918. // $newOptionsArray = array();
  919. // $newProduct->setCanSaveCustomOptions(true);
  920. // foreach ($this->getOptions() as $_option) {
  921. // /* @var $_option Mage_Catalog_Model_Product_Option */
  922. // $newOptionsArray[] = $_option->prepareOptionForDuplicate();
  923. // }
  924. // $newProduct->setProductOptions($newOptionsArray);
  925. /* Prepare Related*/
  926. $data = array();
  927. $this->getLinkInstance()->useRelatedLinks();
  928. $attributes = array();
  929. foreach ($this->getLinkInstance()->getAttributes() as $_attribute) {
  930. if (isset($_attribute['code'])) {
  931. $attributes[]=$_attribute['code'];
  932. }
  933. }
  934. foreach ($this->getRelatedLinkCollection() as $_link) {
  935. $data[$_link->getLinkedProductId()] = $_link->toArray($attributes);
  936. }
  937. $newProduct->setRelatedLinkData($data);
  938. /* Prepare UpSell*/
  939. $data = array();
  940. $this->getLinkInstance()->useUpSellLinks();
  941. $attributes = array();
  942. foreach ($this->getLinkInstance()->getAttributes() as $_attribute) {
  943. if (isset($_attribute['code'])) {
  944. $attributes[]=$_attribute['code'];
  945. }
  946. }
  947. foreach ($this->getUpSellLinkCollection() as $_link) {
  948. $data[$_link->getLinkedProductId()] = $_link->toArray($attributes);
  949. }
  950. $newProduct->setUpSellLinkData($data);
  951. /* Prepare Cross Sell */
  952. $data = array();
  953. $this->getLinkInstance()->useCrossSellLinks();
  954. $attributes = array();
  955. foreach ($this->getLinkInstance()->getAttributes() as $_attribute) {
  956. if (isset($_attribute['code'])) {
  957. $attributes[]=$_attribute['code'];
  958. }
  959. }
  960. foreach ($this->getCrossSellLinkCollection() as $_link) {
  961. $data[$_link->getLinkedProductId()] = $_link->toArray($attributes);
  962. }
  963. $newProduct->setCrossSellLinkData($data);
  964. /* Prepare Grouped */
  965. $data = array();
  966. $this->getLinkInstance()->useGroupedLinks();
  967. $attributes = array();
  968. foreach ($this->getLinkInstance()->getAttributes() as $_attribute) {
  969. if (isset($_attribute['code'])) {
  970. $attributes[]=$_attribute['code'];
  971. }
  972. }
  973. foreach ($this->getGroupedLinkCollection() as $_link) {
  974. $data[$_link->getLinkedProductId()] = $_link->toArray($attributes);
  975. }
  976. $newProduct->setGroupedLinkData($data);
  977. $newProduct->save();
  978. $this->getOptionInstance()->duplicate($this->getId(), $newProduct->getId());
  979. $this->getResource()->duplicate($this->getId(), $newProduct->getId());
  980. // TODO - duplicate product on all stores of the websites it is associated with
  981. /*if ($storeIds = $this->getWebsiteIds()) {
  982. foreach ($storeIds as $storeId) {
  983. $this->setStoreId($storeId)
  984. ->load($this->getId());
  985. $newProduct->setData($this->getData())
  986. ->setSku(null)
  987. ->setStatus(Mage_Catalog_Model_Product_Status::STATUS_DISABLED)
  988. ->setId($newId)
  989. ->save();
  990. }
  991. }*/
  992. return $newProduct;
  993. }
  994. public function isSuperGroup()
  995. {
  996. return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED;
  997. }
  998. public function isSuperConfig()
  999. {
  1000. return $this->isConfigurable();
  1001. }
  1002. /**
  1003. * Check is product grouped
  1004. *
  1005. * @return bool
  1006. */
  1007. public function isGrouped()
  1008. {
  1009. return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_GROUPED;
  1010. }
  1011. /**
  1012. * Check is product configurable
  1013. *
  1014. * @return bool
  1015. */
  1016. public function isConfigurable()
  1017. {
  1018. return $this->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE;
  1019. }
  1020. public function isSuper()
  1021. {
  1022. return $this->isConfigurable() || $this->isGrouped();
  1023. }
  1024. public function getVisibleInCatalogStatuses()
  1025. {
  1026. return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds();
  1027. }
  1028. /**
  1029. * Retrieve visible statuses
  1030. *
  1031. * @return array
  1032. */
  1033. public function getVisibleStatuses()
  1034. {
  1035. return Mage::getSingleton('catalog/product_status')->getVisibleStatusIds();
  1036. }
  1037. /**
  1038. * Check Product visilbe in catalog
  1039. *
  1040. * @return bool
  1041. */
  1042. public function isVisibleInCatalog()
  1043. {
  1044. return in_array($this->getStatus(), $this->getVisibleInCatalogStatuses());
  1045. }
  1046. /**
  1047. * Retrieve visible in site visibilities
  1048. *
  1049. * @return array
  1050. */
  1051. public function getVisibleInSiteVisibilities()
  1052. {
  1053. return Mage::getSingleton('catalog/product_visibility')->getVisibleInSiteIds();
  1054. }
  1055. /**
  1056. * Check Product visible in site
  1057. *
  1058. * @return bool
  1059. */
  1060. public function isVisibleInSiteVisibility()
  1061. {
  1062. return in_array($this->getVisibility(), $this->getVisibleInSiteVisibilities());
  1063. }
  1064. /**
  1065. * Checks product can be duplicated
  1066. *
  1067. * @return boolean
  1068. */
  1069. public function isDuplicable()
  1070. {
  1071. return $this->_isDuplicable;
  1072. }
  1073. /**
  1074. * Set is duplicable flag
  1075. *
  1076. * @param boolean $value
  1077. * @return Mage_Catalog_Model_Product
  1078. */
  1079. public function setIsDuplicable($value)
  1080. {
  1081. $this->_isDuplicable = (boolean) $value;
  1082. return $this;
  1083. }
  1084. /**
  1085. * Check is product available for sale
  1086. *
  1087. * @return bool
  1088. */
  1089. public function isSalable()
  1090. {
  1091. Mage::dispatchEvent('catalog_product_is_salable_before', array(
  1092. 'product' => $this
  1093. ));
  1094. $salable = $this->isAvailable();
  1095. $object = new Varien_Object(array(
  1096. 'product' => $this,
  1097. 'is_salable' => $salable
  1098. ));
  1099. Mage::dispatchEvent('catalog_product_is_salable_after', array(
  1100. 'product' => $this,
  1101. 'salable' => $object
  1102. ));
  1103. return $object->getIsSalable();
  1104. }
  1105. /**
  1106. * Check whether the product type or stock allows to purchase the product
  1107. *
  1108. * @return bool
  1109. */
  1110. public function isAvailable()
  1111. {
  1112. return $this->getTypeInstance(true)->isSalable($this);
  1113. }
  1114. /**
  1115. * Check is a virtual product
  1116. * Data helper wraper
  1117. *
  1118. * @return bool
  1119. */
  1120. public function isVirtual()
  1121. {
  1122. return $this->getIsVirtual();
  1123. }
  1124. /**
  1125. * Whether the product is a recurring payment
  1126. *
  1127. * @return bool
  1128. */
  1129. public function isRecurring()
  1130. {
  1131. return $this->getIsRecurring() == '1';
  1132. }
  1133. public function isSaleable()
  1134. {
  1135. return $this->isSalable();
  1136. }
  1137. public function isInStock()
  1138. {
  1139. return $this->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED;
  1140. }
  1141. public function getAttributeText($attributeCode)
  1142. {
  1143. return $this->getResource()
  1144. ->getAttribute($attributeCode)
  1145. ->getSource()
  1146. ->getOptionText($this->getData($attributeCode));
  1147. }
  1148. public function getCustomDesignDate()
  1149. {
  1150. $result = array();
  1151. $result['from'] = $this->getData('custom_design_from');
  1152. $result['to'] = $this->getData('custom_design_to');
  1153. return $result;
  1154. }
  1155. /**
  1156. * Retrieve Product URL
  1157. *
  1158. * @param bool $useSid
  1159. * @return string
  1160. */
  1161. public function getProductUrl($useSid = null)
  1162. {
  1163. return $this->getUrlModel()->getProductUrl($this, $useSid);
  1164. }
  1165. /**
  1166. * Retrieve URL in current store
  1167. *
  1168. * @param array $params the route params
  1169. * @return string
  1170. */
  1171. public function getUrlInStore($params = array())
  1172. {
  1173. return $this->getUrlModel()->getUrlInStore($this, $params);
  1174. }
  1175. public function formatUrlKey($str)
  1176. {
  1177. return $this->getUrlModel()->formatUrlKey($str);
  1178. }
  1179. /**
  1180. * Retrieve Product Url Path (include category)
  1181. *
  1182. * @param Mage_Catalog_Model_Category $category
  1183. * @return string
  1184. */
  1185. public function getUrlPath($category=null)
  1186. {
  1187. return $this->getUrlModel()->getUrlPath($this, $category);
  1188. }
  1189. public function addAttributeUpdate($code, $value, $store)
  1190. {
  1191. $oldValue = $this->getData($code);
  1192. $oldStore = $this->getStoreId();
  1193. $this->setData($code, $value);
  1194. $this->setStoreId($store);
  1195. $this->getResource()->saveAttribute($this, $code);
  1196. $this->setData($code, $oldValue);
  1197. $this->setStoreId($oldStore);
  1198. }
  1199. public function toArray(array $arrAttributes=array())
  1200. {
  1201. $data = parent::toArray($arrAttributes);
  1202. if ($stock = $this->getStockItem()) {
  1203. $data['stock_item'] = $stock->toArray();
  1204. }
  1205. unset($data['stock_item']['product']);
  1206. return $data;
  1207. }
  1208. public function fromArray($data)
  1209. {
  1210. if (isset($data['stock_item'])) {
  1211. $stockItem = Mage::getModel('cataloginventory/stock_item')
  1212. ->setData($data['stock_item'])
  1213. ->setProduct($this);
  1214. $this->setStockItem($stockItem);
  1215. unset($data['stock_item']);
  1216. }
  1217. $this->setData($data);
  1218. return $this;
  1219. }
  1220. /**
  1221. * @deprecated after 1.4.2.0
  1222. * @return Mage_Catalog_Model_Product
  1223. */
  1224. public function loadParentProductIds()
  1225. {
  1226. return $this->setParentProductIds(array());
  1227. }
  1228. public function delete()
  1229. {
  1230. parent::delete();
  1231. Mage::dispatchEvent($this->_eventPrefix.'_delete_after_done', array($this->_eventObject=>$this));
  1232. return $this;
  1233. }
  1234. public function getRequestPath()
  1235. {
  1236. return $this->_getData('request_path');
  1237. }
  1238. /**
  1239. * Custom function for other modules
  1240. */
  1241. public function getGiftMessageAvailable()
  1242. {
  1243. return $this->_getData('gift_message_available');
  1244. }
  1245. public function getRatingSummary()
  1246. {
  1247. return $this->_getData('rating_summary');
  1248. }
  1249. /**
  1250. * Check is product composite
  1251. *
  1252. * @return bool
  1253. */
  1254. public function isComposite()
  1255. {
  1256. return $this->getTypeInstance(true)->isComposite($this);
  1257. }
  1258. /**
  1259. * Check if product can be configured
  1260. *
  1261. * @return bool
  1262. */
  1263. public function canConfigure()
  1264. {
  1265. $options = $this->getOptions();
  1266. return !empty($options) || $this->getTypeInstance(true)->canConfigure($this);
  1267. }
  1268. /**
  1269. * Retrieve sku through type instance
  1270. *
  1271. * @return string
  1272. */
  1273. public function getSku()
  1274. {
  1275. return $this->getTypeInstance(true)->getSku($this);
  1276. }
  1277. /**
  1278. * Retrieve weight throught type instance
  1279. *
  1280. * @return unknown
  1281. */
  1282. public function getWeight()
  1283. {
  1284. return $this->getTypeInstance(true)->getWeight($this);
  1285. }
  1286. /**
  1287. * Retrieve option instance
  1288. *
  1289. * @return Mage_Catalog_Model_Product_Option
  1290. */
  1291. public function getOptionInstance()
  1292. {
  1293. if (!$this->_optionInstance) {
  1294. $this->_optionInstance = Mage::getSingleton('catalog/product_option');
  1295. }
  1296. return $this->_optionInstance;
  1297. }
  1298. /**
  1299. * Retrieve options collection of product
  1300. *
  1301. * @return Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Option_Collection
  1302. */
  1303. public function getProductOptionsCollection()
  1304. {
  1305. $collection = $this->getOptionInstance()
  1306. ->getProductOptionCollection($this);
  1307. return $collection;
  1308. }
  1309. /**
  1310. * Add option to array of product options
  1311. *
  1312. * @param Mage_Catalog_Model_Product_Option $option
  1313. * @return Mage_Catalog_Model_Product
  1314. */
  1315. public function addOption(Mage_Catalog_Model_Product_Option $option)
  1316. {
  1317. $this->_options[$option->getId()] = $option;
  1318. return $this;
  1319. }
  1320. /**
  1321. * Get option from options array of product by given option id
  1322. *
  1323. * @param int $optionId
  1324. * @return Mage_Catalog_Model_Product_Option | null
  1325. */
  1326. public function getOptionById($optionId)
  1327. {
  1328. if (isset($this->_options[$optionId])) {
  1329. return $this->_options[$optionId];
  1330. }
  1331. return null;
  1332. }
  1333. /**
  1334. * Get all options of product
  1335. *
  1336. * @return array
  1337. */
  1338. public function getOptions()
  1339. {
  1340. return $this->_options;
  1341. }
  1342. /**
  1343. * Retrieve is a virtual product
  1344. *
  1345. * @return bool
  1346. */
  1347. public function getIsVirtual()
  1348. {
  1349. return $this->getTypeInstance(true)->isVirtual($this);
  1350. }
  1351. /**
  1352. * Add custom option information to product
  1353. *
  1354. * @param string $code
  1355. * @param mixed $value
  1356. * @param int $productId
  1357. * @return Mage_Catalog_Model_Product
  1358. */
  1359. public function addCustomOption($code, $value, $product=null)
  1360. {
  1361. $product = $product ? $product : $this;
  1362. $option = Mage::getModel('catalog/product_configuration_item_option')
  1363. ->addData(array(
  1364. 'product_id'=> $product->getId(),
  1365. 'product' => $product,
  1366. 'code' => $code,
  1367. 'value' => $value,
  1368. ));
  1369. $this->_customOptions[$code] = $option;
  1370. return $this;
  1371. }
  1372. public function setCustomOptions(array $options)
  1373. {
  1374. $this->_customOptions = $options;
  1375. }
  1376. /**
  1377. * Get all custom options of the product
  1378. *
  1379. * @return array
  1380. */
  1381. public function getCustomOptions()
  1382. {
  1383. return $this->_customOptions;
  1384. }
  1385. /**
  1386. * Get product custom option info
  1387. *
  1388. * @param string $code
  1389. * @return array
  1390. */
  1391. public function getCustomOption($code)
  1392. {
  1393. if (isset($this->_customOptions[$code])) {
  1394. return $this->_customOptions[$code];
  1395. }
  1396. return null;
  1397. }
  1398. /**
  1399. * Checks if there custom option for this product
  1400. *
  1401. * @return bool
  1402. */
  1403. public function hasCustomOptions()
  1404. {
  1405. if (count($this->_customOptions)) {
  1406. return true;
  1407. } else {
  1408. return false;
  1409. }
  1410. }
  1411. /**
  1412. * Check availability display product in category
  1413. *
  1414. * @param int $categoryId
  1415. * @return bool
  1416. */
  1417. public function canBeShowInCategory($categoryId)
  1418. {
  1419. return $this->_getResource()->canBeShowInCategory($this, $categoryId);
  1420. }
  1421. /**
  1422. * Retrieve category ids where product is available
  1423. *
  1424. * @return array
  1425. */
  1426. public function getAvailableInCategories()
  1427. {
  1428. return $this->_getResource()->getAvailableInCategories($this);
  1429. }
  1430. /**
  1431. * Retrieve default attribute set id
  1432. *
  1433. * @return int
  1434. */
  1435. public function getDefaultAttributeSetId()
  1436. {
  1437. return $this->getResource()->getEntityType()->getDefaultAttributeSetId();
  1438. }
  1439. /**
  1440. * Deprecated since 1.1.5
  1441. */
  1442. public function getImageUrl()
  1443. {
  1444. return (string)Mage::helper('catalog/image')->init($this, 'image')->resize(265);
  1445. }
  1446. /**
  1447. * Deprecated since 1.1.5
  1448. */
  1449. public function getSmallImageUrl($width = 88, $height = 77)
  1450. {
  1451. return (string)Mage::helper('catalog/image')->init($this, 'small_image')->resize($width, $height);
  1452. }
  1453. /**
  1454. * Deprecated since 1.1.5
  1455. */
  1456. public function getThumbnailUrl($width = 75, $height = 75)
  1457. {
  1458. return (string)Mage::helper('catalog/image')->init($this, 'thumbnail')->resize($width, $height);
  1459. }
  1460. /**
  1461. * Returns system reserved attribute codes
  1462. *
  1463. * @return array Reserved attribute names
  1464. */
  1465. public function getReservedAttributes()
  1466. {
  1467. if ($this->_reservedAttributes === null) {
  1468. $_reserved = array('position');
  1469. $methods = get_class_methods(__CLASS__);
  1470. foreach ($methods as $method) {
  1471. if (preg_match('/^get([A-Z]{1}.+)/', $method, $matches)) {
  1472. $method = $matches[1];
  1473. $tmp = strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $method));
  1474. $_reserved[] = $tmp;
  1475. }
  1476. }
  1477. $_allowed = array(
  1478. 'type_id','calculated_final_price','request_path','rating_summary'
  1479. );
  1480. $this->_reservedAttributes = array_diff($_reserved, $_allowed);
  1481. }
  1482. return $this->_reservedAttributes;
  1483. }
  1484. /**
  1485. * Check whether attribute reserved or not
  1486. *
  1487. * @param Mage_Eav_Model_Entity_Attribute $attribute Attribute model object
  1488. * @return boolean
  1489. */
  1490. public function isReservedAttribute ($attribute)
  1491. {
  1492. return $attribute->getIsUserDefined()
  1493. && in_array($attribute->getAttributeCode(), $this->getReservedAttributes());
  1494. }
  1495. /**
  1496. * Set original loaded data if needed
  1497. *
  1498. * @param string $key
  1499. * @param mixed $data
  1500. * @return Varien_Object
  1501. */
  1502. public function setOrigData($key=null, $data=null)
  1503. {
  1504. if (Mage::app()->getStore()->isAdmin()) {
  1505. return parent::setOrigData($key, $data);
  1506. }
  1507. return $this;
  1508. }
  1509. /**
  1510. * @deprecated
  1511. * @see Mage_Sales_Model_Observer::substractQtyFromQuotes()
  1512. */
  1513. protected function _substractQtyFromQuotes()
  1514. {
  1515. // kept for legacy purposes
  1516. }
  1517. /**
  1518. * Reset all model data
  1519. *
  1520. * @return Mage_Catalog_Model_Product
  1521. */
  1522. public function reset()
  1523. {
  1524. $this->_clearData();
  1525. return $this;
  1526. }
  1527. /**
  1528. * Get cahce tags associated with object id
  1529. *
  1530. * @return array
  1531. */
  1532. public function getCacheIdTags()
  1533. {
  1534. $tags = parent::getCacheIdTags();
  1535. $affectedCategoryIds = $this->getAffectedCategoryIds();
  1536. if (!$affectedCategoryIds) {
  1537. $affectedCategoryIds = $this->getCategoryIds();
  1538. }
  1539. foreach ($affectedCategoryIds as $categoryId) {
  1540. $tags[] = Mage_Catalog_Model_Category::CACHE_TAG.'_'.$categoryId;
  1541. }
  1542. return $tags;
  1543. }
  1544. /**
  1545. * Check for empty SKU on each product
  1546. *
  1547. * @param array $productIds
  1548. * @return boolean|null
  1549. */
  1550. public function isProductsHasSku(array $productIds)
  1551. {
  1552. $products = $this->_getResource()->getProductsSku($productIds);
  1553. if (count($products)) {
  1554. foreach ($products as $product) {
  1555. if (empty($product['sku'])) {
  1556. return false;
  1557. }
  1558. }
  1559. return true;
  1560. }
  1561. return null;
  1562. }
  1563. /**
  1564. * Parse buyRequest into options values used by product
  1565. *
  1566. * @param Varien_Object $buyRequest
  1567. * @return Varien_Object
  1568. */
  1569. public function processBuyRequest(Varien_Object $buyRequest)
  1570. {
  1571. $options = new Varien_Object();
  1572. /* add product custom options data */
  1573. $customOptions = $buyRequest->getOptions();
  1574. if (is_array($customOptions)) {
  1575. $options->setOptions(array_diff($buyRequest->getOptions(), array('')));
  1576. }
  1577. /* add product type selected options data */
  1578. $type = $this->getTypeInstance(true);
  1579. $typeSpecificOptions = $type->processBuyRequest($this, $buyRequest);
  1580. $options->addData($typeSpecificOptions);
  1581. /* check correctness of product's options */
  1582. $options->setErrors($type->checkProductConfiguration($this, $buyRequest));
  1583. return $options;
  1584. }
  1585. /**
  1586. * Get preconfigured values from product
  1587. *
  1588. * @return Varien_Object
  1589. */
  1590. public function getPreconfiguredValues()
  1591. {
  1592. $preconfiguredValues = $this->getData('preconfigured_values');
  1593. if (!$preconfiguredValues) {
  1594. $preconfiguredValues = new Varien_Object();
  1595. }
  1596. return $preconfiguredValues;
  1597. }
  1598. /**
  1599. * Prepare product custom options.
  1600. * To be sure that all product custom options does not has ID and has product instance
  1601. *
  1602. * @return Mage_Catalog_Model_Product
  1603. */
  1604. public function prepareCustomOptions()
  1605. {
  1606. foreach ($this->getCustomOptions() as $option) {
  1607. if (!is_object($option->getProduct()) || $option->getId()) {
  1608. $this->addCustomOption($option->getCode(), $option->getValue());
  1609. }
  1610. }
  1611. return $this;
  1612. }
  1613. /**
  1614. * Clearing references on product
  1615. *
  1616. * @return Mage_Catalog_Model_Product
  1617. */
  1618. protected function _clearReferences()
  1619. {
  1620. $this->_clearOptionReferences();
  1621. return $this;
  1622. }
  1623. /**
  1624. * Clearing product's data
  1625. *
  1626. * @return Mage_Catalog_Model_Product
  1627. */
  1628. protected function _clearData()
  1629. {
  1630. foreach ($this->_data as $data){
  1631. if (is_object($data) && method_exists($data, 'reset')){
  1632. $data->reset();
  1633. }
  1634. }
  1635. $this->setData(array());
  1636. $this->setOrigData();
  1637. $this->_customOptions = array();
  1638. $this->_optionInstance = null;
  1639. $this->_options = array();
  1640. $this->_canAffectOptions = false;
  1641. $this->_errors = array();
  1642. return $this;
  1643. }
  1644. /**
  1645. * Clearing references to product from product's options
  1646. *
  1647. * @return Mage_Catalog_Model_Product
  1648. */
  1649. protected function _clearOptionReferences()
  1650. {
  1651. /**
  1652. * unload product options
  1653. */
  1654. if (!empty($this->_options)) {
  1655. foreach ($this->_options as $key => $option) {
  1656. $option->setProduct();
  1657. $option->clearInstance();
  1658. }
  1659. }
  1660. return $this;
  1661. }
  1662. }